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

no content

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
--- a/js-packages/tests/src/transfer.test.ts
+++ /dev/null
@@ -1,345 +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 './eth/util/index.js';
-import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/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);
-  });
-});
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"