git.delta.rocks / unique-network / refs/commits / d035b5d10ee1

difftreelog

Fixes after review

Max Andreev2023-03-29parent: #1550f8c.patch.diff
in: master

6 files changed

modified.docker/xcm-config/launch-config-xcm-quartz.j2diffbeforeafterboth
--- a/.docker/xcm-config/launch-config-xcm-quartz.j2
+++ b/.docker/xcm-config/launch-config-xcm-quartz.j2
@@ -169,7 +169,7 @@
         {
             "bin": "/astar/target/release/astar",
             "id": "2007",
-            "chain": "astar-dev",
+            "chain": "shiden-dev",
             "balance": "1000000000000000000000000",
             "nodes": [
                 {
@@ -224,12 +224,12 @@
         },
         {
             "sender": 2007,
-            "recipient": 1000,
+            "recipient": 2095,
             "maxCapacity": 8,
             "maxMessageSize": 512
         },
         {
-            "sender": 1000,
+            "sender": 2095,
             "recipient": 2007,
             "maxCapacity": 8,
             "maxMessageSize": 512
modified.docker/xcm-config/launch-config-xcm-unique.j2diffbeforeafterboth
--- a/.docker/xcm-config/launch-config-xcm-unique.j2
+++ b/.docker/xcm-config/launch-config-xcm-unique.j2
@@ -224,12 +224,12 @@
         },
         {
             "sender": 2006,
-            "recipient": 1000,
+            "recipient": 2037,
             "maxCapacity": 8,
             "maxMessageSize": 512
         },
         {
-            "sender": 1000,
+            "sender": 2037,
             "recipient": 2006,
             "maxCapacity": 8,
             "maxMessageSize": 512
modifiedtests/src/util/index.tsdiffbeforeafterboth
--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -11,7 +11,7 @@
 import config from '../config';
 import {ChainHelperBase} from './playgrounds/unique';
 import {ILogger} from './playgrounds/types';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper} from './playgrounds/unique.dev';
+import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper} from './playgrounds/unique.dev';
 import {dirname} from 'path';
 import {fileURLToPath} from 'url';
 
@@ -105,6 +105,9 @@
   return usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);
 };
 
+export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+  return usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
+};
 
 export const MINIMUM_DONOR_FUND = 100_000n;
 export const DONOR_FUNDING = 2_000_000n;
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
after · tests/src/util/playgrounds/unique.dev.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import {stringToU8a} from '@polkadot/util';5import {encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';6import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper, AstarHelper} from './unique';7import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';8import * as defs from '../../interfaces/definitions';9import {IKeyringPair} from '@polkadot/types/types';10import {EventRecord} from '@polkadot/types/interfaces';11import {ICrossAccountId, IPovInfo, TSigner} from './types';12import {FrameSystemEventRecord} from '@polkadot/types/lookup';13import {VoidFn} from '@polkadot/api/types';14import {Pallets} from '..';15import {spawnSync} from 'child_process';1617export class SilentLogger {18  log(_msg: any, _level: any): void { }19  level = {20    ERROR: 'ERROR' as const,21    WARNING: 'WARNING' as const,22    INFO: 'INFO' as const,23  };24}2526export class SilentConsole {27  // TODO: Remove, this is temporary: Filter unneeded API output28  // (Jaco promised it will be removed in the next version)29  consoleErr: any;30  consoleLog: any;31  consoleWarn: any;3233  constructor() {34    this.consoleErr = console.error;35    this.consoleLog = console.log;36    this.consoleWarn = console.warn;37  }3839  enable() {40    const outFn = (printer: any) => (...args: any[]) => {41      for (const arg of args) {42        if (typeof arg !== 'string')43          continue;44        if (arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure')45          return;46      }47      printer(...args);48    };4950    console.error = outFn(this.consoleErr.bind(console));51    console.log = outFn(this.consoleLog.bind(console));52    console.warn = outFn(this.consoleWarn.bind(console));53  }5455  disable() {56    console.error = this.consoleErr;57    console.log = this.consoleLog;58    console.warn = this.consoleWarn;59  }60}6162export class DevUniqueHelper extends UniqueHelper {63  /**64   * Arrange methods for tests65   */66  arrange: ArrangeGroup;67  wait: WaitGroup;68  admin: AdminGroup;69  session: SessionGroup;70  testUtils: TestUtilGroup;7172  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {73    options.helperBase = options.helperBase ?? DevUniqueHelper;7475    super(logger, options);76    this.arrange = new ArrangeGroup(this);77    this.wait = new WaitGroup(this);78    this.admin = new AdminGroup(this);79    this.testUtils = new TestUtilGroup(this);80    this.session = new SessionGroup(this);81  }8283  async connect(wsEndpoint: string, _listeners?: any): Promise<void> {84    const wsProvider = new WsProvider(wsEndpoint);85    this.api = new ApiPromise({86      provider: wsProvider,87      signedExtensions: {88        ContractHelpers: {89          extrinsic: {},90          payload: {},91        },92        CheckMaintenance: {93          extrinsic: {},94          payload: {},95        },96        DisableIdentityCalls: {97          extrinsic: {},98          payload: {},99        },100        FakeTransactionFinalizer: {101          extrinsic: {},102          payload: {},103        },104      },105      rpc: {106        unique: defs.unique.rpc,107        appPromotion: defs.appPromotion.rpc,108        povinfo: defs.povinfo.rpc,109        eth: {110          feeHistory: {111            description: 'Dummy',112            params: [],113            type: 'u8',114          },115          maxPriorityFeePerGas: {116            description: 'Dummy',117            params: [],118            type: 'u8',119          },120        },121      },122    });123    await this.api.isReadyOrError;124    this.network = await UniqueHelper.detectNetwork(this.api);125    this.wsEndpoint = wsEndpoint;126  }127}128129export class DevRelayHelper extends RelayHelper {130  wait: WaitGroup;131132  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {133    options.helperBase = options.helperBase ?? DevRelayHelper;134135    super(logger, options);136    this.wait = new WaitGroup(this);137  }138}139140export class DevWestmintHelper extends WestmintHelper {141  wait: WaitGroup;142143  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {144    options.helperBase = options.helperBase ?? DevWestmintHelper;145146    super(logger, options);147    this.wait = new WaitGroup(this);148  }149}150151export class DevStatemineHelper extends DevWestmintHelper {}152153export class DevStatemintHelper extends DevWestmintHelper {}154155export class DevMoonbeamHelper extends MoonbeamHelper {156  account: MoonbeamAccountGroup;157  wait: WaitGroup;158159  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {160    options.helperBase = options.helperBase ?? DevMoonbeamHelper;161    options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';162163    super(logger, options);164    this.account = new MoonbeamAccountGroup(this);165    this.wait = new WaitGroup(this);166  }167}168169export class DevMoonriverHelper extends DevMoonbeamHelper {170  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {171    options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';172    super(logger, options);173  }174}175176export class DevAstarHelper extends AstarHelper {177  wait: WaitGroup;178179  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {180    options.helperBase = options.helperBase ?? DevAstarHelper;181182    super(logger, options);183    this.wait = new WaitGroup(this);184  }185}186187export class DevShidenHelper extends AstarHelper {188  wait: WaitGroup;189190  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {191    options.helperBase = options.helperBase ?? DevShidenHelper;192193    super(logger, options);194    this.wait = new WaitGroup(this);195  }196}197198export class DevAcalaHelper extends AcalaHelper {199  wait: WaitGroup;200201  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {202    options.helperBase = options.helperBase ?? DevAcalaHelper;203204    super(logger, options);205    this.wait = new WaitGroup(this);206  }207}208209export class DevKaruraHelper extends DevAcalaHelper {}210211export class ArrangeGroup {212  helper: DevUniqueHelper;213214  scheduledIdSlider = 0;215216  constructor(helper: DevUniqueHelper) {217    this.helper = helper;218  }219220  /**221   * Generates accounts with the specified UNQ token balance222   * @param balances balances for generated accounts. Each balance will be multiplied by the token nominal.223   * @param donor donor account for balances224   * @returns array of newly created accounts225   * @example const [acc1, acc2, acc3] = await createAccounts([0n, 10n, 20n], donor);226   */227  createAccounts = async (balances: bigint[], donor: IKeyringPair): Promise<IKeyringPair[]> => {228    let nonce = await this.helper.chain.getNonce(donor.address);229    const wait = new WaitGroup(this.helper);230    const ss58Format = this.helper.chain.getChainProperties().ss58Format;231    const tokenNominal = this.helper.balance.getOneTokenNominal();232    const transactions = [];233    const accounts: IKeyringPair[] = [];234    for (const balance of balances) {235      const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);236      accounts.push(recipient);237      if (balance !== 0n) {238        const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, balance * tokenNominal]);239        transactions.push(this.helper.signTransaction(donor, tx, {nonce, era: 0}, 'account generation'));240        nonce++;241      }242    }243244    await Promise.all(transactions).catch(_e => {});245246    //#region TODO remove this region, when nonce problem will be solved247    const checkBalances = async () => {248      let isSuccess = true;249      for (let i = 0; i < balances.length; i++) {250        const balance = await this.helper.balance.getSubstrate(accounts[i].address);251        if (balance !== balances[i] * tokenNominal) {252          isSuccess = false;253          break;254        }255      }256      return isSuccess;257    };258259    let accountsCreated = false;260    const maxBlocksChecked = await this.helper.arrange.isDevNode() ? 50 : 5;261    // checkBalances retry up to 5-50 blocks262    for (let index = 0; index < maxBlocksChecked; index++) {263      accountsCreated = await checkBalances();264      if(accountsCreated) break;265      await wait.newBlocks(1);266    }267268    if (!accountsCreated) throw Error('Accounts generation failed');269    //#endregion270271    return accounts;272  };273274  // TODO combine this method and createAccounts into one275  createCrowd = async (accountsToCreate: number, withBalance: bigint, donor: IKeyringPair): Promise<IKeyringPair[]> => {276    const createAsManyAsCan = async () => {277      let transactions: any = [];278      const accounts: IKeyringPair[] = [];279      let nonce = await this.helper.chain.getNonce(donor.address);280      const tokenNominal = this.helper.balance.getOneTokenNominal();281      const ss58Format = this.helper.chain.getChainProperties().ss58Format;282      for (let i = 0; i < accountsToCreate; i++) {283        if (i === 500) { // if there are too many accounts to create284          await Promise.allSettled(transactions); // wait while first 500 (should be 100 for devnode) tx will be settled285          transactions = []; //286          nonce = await this.helper.chain.getNonce(donor.address); // update nonce287        }288        const recipient = this.helper.util.fromSeed(mnemonicGenerate(), ss58Format);289        accounts.push(recipient);290        if (withBalance !== 0n) {291          const tx = this.helper.constructApiCall('api.tx.balances.transfer', [{Id: recipient.address}, withBalance * tokenNominal]);292          transactions.push(this.helper.signTransaction(donor, tx, {nonce}, 'account generation'));293          nonce++;294        }295      }296297      const fullfilledAccounts = [];298      await Promise.allSettled(transactions);299      for (const account of accounts) {300        const accountBalance = await this.helper.balance.getSubstrate(account.address);301        if (accountBalance === withBalance * tokenNominal) {302          fullfilledAccounts.push(account);303        }304      }305      return fullfilledAccounts;306    };307308309    const crowd: IKeyringPair[] = [];310    // do up to 5 retries311    for (let index = 0; index < 5 && accountsToCreate !== 0; index++) {312      const asManyAsCan = await createAsManyAsCan();313      crowd.push(...asManyAsCan);314      accountsToCreate -= asManyAsCan.length;315    }316317    if (accountsToCreate !== 0) throw Error(`Crowd generation failed: ${accountsToCreate} accounts left`);318319    return crowd;320  };321322  isDevNode = async () => {323    let blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();324    if (blockNumber == 0) {325      await this.helper.wait.newBlocks(1);326      blockNumber = (await this.helper.callRpc('api.query.system.number')).toJSON();327    }328    const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]);329    const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]);330    const findCreationDate = (block: any) => {331      const humanBlock = block.toHuman();332      let date;333      humanBlock.block.extrinsics.forEach((ext: any) => {334        if(ext.method.section === 'timestamp') {335          date = Number(ext.method.args.now.replaceAll(',', ''));336        }337      });338      return date;339    };340    const block1date = await findCreationDate(block1);341    const block2date = await findCreationDate(block2);342    if(block2date! - block1date! < 9000) return true;343  };344345  async calculcateFee(payer: ICrossAccountId, promise: () => Promise<any>): Promise<bigint> {346    const address = payer.Substrate ? payer.Substrate : this.helper.address.ethToSubstrate(payer.Ethereum!);347    let balance = await this.helper.balance.getSubstrate(address);348349    await promise();350351    balance -= await this.helper.balance.getSubstrate(address);352353    return balance;354  }355356  async calculatePoVInfo(txs: any[]): Promise<IPovInfo> {357    const rawPovInfo = await this.helper.callRpc('api.rpc.povinfo.estimateExtrinsicPoV', [txs]);358359    const kvJson: {[key: string]: string} = {};360361    for (const kv of rawPovInfo.keyValues) {362      kvJson[kv.key.toHex()] = kv.value.toHex();363    }364365    const kvStr = JSON.stringify(kvJson);366367    const chainql = spawnSync(368      'chainql',369      [370        `--tla-code=data=${kvStr}`,371        '-e', `function(data) cql.dump(cql.chain("${this.helper.getEndpoint()}").latest._meta, data, {omit_empty:true})`,372      ],373    );374375    if (!chainql.stdout) {376      throw Error('unable to get an output from the `chainql`');377    }378379    return {380      proofSize: rawPovInfo.proofSize.toNumber(),381      compactProofSize: rawPovInfo.compactProofSize.toNumber(),382      compressedProofSize: rawPovInfo.compressedProofSize.toNumber(),383      results: rawPovInfo.results,384      kv: JSON.parse(chainql.stdout.toString()),385    };386  }387388  calculatePalletAddress(palletId: any) {389    const address = stringToU8a(('modl' + palletId).padEnd(32, '\0'));390    return encodeAddress(address, this.helper.chain.getChainProperties().ss58Format);391  }392393  makeScheduledIds(num: number): string[] {394    function makeId(slider: number) {395      const scheduledIdSize = 64;396      const hexId = slider.toString(16);397      const prefixSize = scheduledIdSize - hexId.length;398399      const scheduledId = '0x' + '0'.repeat(prefixSize) + hexId;400401      return scheduledId;402    }403404    const ids = [];405    for (let i = 0; i < num; i++) {406      ids.push(makeId(this.scheduledIdSlider));407      this.scheduledIdSlider += 1;408    }409410    return ids;411  }412413  makeScheduledId(): string {414    return (this.makeScheduledIds(1))[0];415  }416417  async captureEvents(eventSection: string, eventMethod: string): Promise<EventCapture> {418    const capture = new EventCapture(this.helper, eventSection, eventMethod);419    await capture.startCapture();420421    return capture;422  }423}424425class MoonbeamAccountGroup {426  helper: MoonbeamHelper;427428  keyring: Keyring;429  _alithAccount: IKeyringPair;430  _baltatharAccount: IKeyringPair;431  _dorothyAccount: IKeyringPair;432433  constructor(helper: MoonbeamHelper) {434    this.helper = helper;435436    this.keyring = new Keyring({type: 'ethereum'});437    const alithPrivateKey = '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133';438    const baltatharPrivateKey = '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b';439    const dorothyPrivateKey = '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68';440441    this._alithAccount = this.keyring.addFromUri(alithPrivateKey, undefined, 'ethereum');442    this._baltatharAccount = this.keyring.addFromUri(baltatharPrivateKey, undefined, 'ethereum');443    this._dorothyAccount = this.keyring.addFromUri(dorothyPrivateKey, undefined, 'ethereum');444  }445446  alithAccount() {447    return this._alithAccount;448  }449450  baltatharAccount() {451    return this._baltatharAccount;452  }453454  dorothyAccount() {455    return this._dorothyAccount;456  }457458  create() {459    return this.keyring.addFromUri(mnemonicGenerate());460  }461}462463class WaitGroup {464  helper: ChainHelperBase;465466  constructor(helper: ChainHelperBase) {467    this.helper = helper;468  }469470  sleep(milliseconds: number) {471    return new Promise((resolve) => setTimeout(resolve, milliseconds));472  }473474  private async waitWithTimeout(promise: Promise<any>, timeout: number) {475    let isBlock = false;476    promise.then(() => isBlock = true).catch(() => isBlock = true);477    let totalTime = 0;478    const step = 100;479    while(!isBlock) {480      await this.sleep(step);481      totalTime += step;482      if(totalTime >= timeout) throw Error('Blocks production failed');483    }484    return promise;485  }486487  /**488   * Launch some async operation, or throw an error after some time. Note that it will still continue executing after the timeout.489   * @param promise async operation to race against the timeout490   * @param timeoutMS time after which to time out491   * @param timeoutError error message to throw492   * @returns promise of the same type the operation had493   */494  withTimeout<T>(495    promise: Promise<T>,496    timeoutMS = 30000,497    timeoutError = 'The operation has timed out!',498  ): Promise<T> {499    const timeout = new Promise<never>((_, reject) => {500      setTimeout(() => {501        reject(new Error(timeoutError));502      }, timeoutMS);503    });504505    return Promise.race<T>([promise, timeout]).catch(e => {throw new Error(e);});506  }507508  /**509   * Wait for specified number of blocks510   * @param blocksCount number of blocks to wait511   * @returns512   */513  async newBlocks(blocksCount = 1, timeout?: number): Promise<void> {514    timeout = timeout ?? blocksCount * 60_000;515    // eslint-disable-next-line no-async-promise-executor516    const promise = new Promise<void>(async (resolve) => {517      const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => {518        if (blocksCount > 0) {519          blocksCount--;520        } else {521          unsubscribe();522          resolve();523        }524      });525    });526    await this.waitWithTimeout(promise, timeout);527    return promise;528  }529530  /**531   * Wait for the specified number of sessions to pass.532   * Only applicable if the Session pallet is turned on.533   * @param sessionCount number of sessions to wait534   * @param blockTimeout time in ms until panicking that the chain has stopped producing blocks535   * @returns536   */537  async newSessions(sessionCount = 1, blockTimeout = 60000): Promise<void> {538    console.log(`Waiting for ${sessionCount} new session${sessionCount > 1 ? 's' : ''}.`539      + ' This might take a while -- check SessionPeriod in pallet_session::Config for session time.');540541    const expectedSessionIndex = await (this.helper as DevUniqueHelper).session.getIndex() + sessionCount;542    let currentSessionIndex = -1;543544    while (currentSessionIndex < expectedSessionIndex) {545      // eslint-disable-next-line no-async-promise-executor546      currentSessionIndex = await this.withTimeout(new Promise(async (resolve) => {547        await this.newBlocks(1);548        const res = await (this.helper as DevUniqueHelper).session.getIndex();549        resolve(res);550      }), blockTimeout, 'The chain has stopped producing blocks!');551    }552  }553554  async forParachainBlockNumber(blockNumber: bigint | number, timeout?: number) {555    timeout = timeout ?? 30 * 60 * 1000;556    // eslint-disable-next-line no-async-promise-executor557    const promise = new Promise<void>(async (resolve) => {558      const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {559        if (data.number.toNumber() >= blockNumber) {560          unsubscribe();561          resolve();562        }563      });564    });565    await this.waitWithTimeout(promise, timeout);566    return promise;567  }568569  async forRelayBlockNumber(blockNumber: bigint | number, timeout?: number) {570    timeout = timeout ?? 30 * 60 * 1000;571    // eslint-disable-next-line no-async-promise-executor572    const promise = new Promise<void>(async (resolve) => {573      const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => {574        if (data.value.relayParentNumber.toNumber() >= blockNumber) {575          // @ts-ignore576          unsubscribe();577          resolve();578        }579      });580    });581    await this.waitWithTimeout(promise, timeout);582    return promise;583  }584585  noScheduledTasks() {586    const api = this.helper.getApi();587588    // eslint-disable-next-line no-async-promise-executor589    const promise = new Promise<void>(async resolve => {590      const unsubscribe = await api.rpc.chain.subscribeNewHeads(async () => {591        const areThereScheduledTasks = await api.query.scheduler.lookup.entries();592593        if(areThereScheduledTasks.length == 0) {594          unsubscribe();595          resolve();596        }597      });598    });599600    return promise;601  }602603  event(maxBlocksToWait: number, eventSection: string, eventMethod: string) {604    // eslint-disable-next-line no-async-promise-executor605    const promise = new Promise<EventRecord | null>(async (resolve) => {606      const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {607        const blockNumber = header.number.toHuman();608        const blockHash = header.hash;609        const eventIdStr = `${eventSection}.${eventMethod}`;610        const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;611612        this.helper.logger.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);613614        const apiAt = await this.helper.getApi().at(blockHash);615        const eventRecords = (await apiAt.query.system.events()) as any;616617        const neededEvent = eventRecords.toArray().find((r: FrameSystemEventRecord) => {618          return r.event.section == eventSection && r.event.method == eventMethod;619        });620621        if (neededEvent) {622          unsubscribe();623          resolve(neededEvent);624        } else if (maxBlocksToWait > 0) {625          maxBlocksToWait--;626        } else {627          this.helper.logger.log(`Event \`${eventIdStr}\` is NOT found`);628          unsubscribe();629          resolve(null);630        }631      });632    });633    return promise;634  }635}636637class SessionGroup {638  helper: ChainHelperBase;639640  constructor(helper: ChainHelperBase) {641    this.helper = helper;642  }643644  //todo:collator documentation645  async getIndex(): Promise<number> {646    return (await this.helper.callRpc('api.query.session.currentIndex')).toNumber();647  }648649  newSessions(sessionCount = 1, blockTimeout = 24000): Promise<void> {650    return (this.helper as DevUniqueHelper).wait.newSessions(sessionCount, blockTimeout);651  }652653  setOwnKeys(signer: TSigner, key: string) {654    return this.helper.executeExtrinsic(655      signer,656      'api.tx.session.setKeys',657      [key, '0x0'],658      true,659    );660  }661662  setOwnKeysFromAddress(signer: TSigner) {663    return this.setOwnKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));664  }665}666667class TestUtilGroup {668  helper: DevUniqueHelper;669670  constructor(helper: DevUniqueHelper) {671    this.helper = helper;672  }673674  async enable() {675    if (this.helper.fetchMissingPalletNames([Pallets.TestUtils]).length != 0) {676      return;677    }678679    const signer = this.helper.util.fromSeed('//Alice');680    await this.helper.getSudo<DevUniqueHelper>().executeExtrinsic(signer, 'api.tx.testUtils.enable', [], true);681  }682683  async setTestValue(signer: TSigner, testVal: number) {684    await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValue', [testVal], true);685  }686687  async incTestValue(signer: TSigner) {688    await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.incTestValue', [], true);689  }690691  async setTestValueAndRollback(signer: TSigner, testVal: number) {692    await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.setTestValueAndRollback', [testVal], true);693  }694695  async testValue(blockIdx?: number) {696    const api = blockIdx697      ? await this.helper.getApi().at(await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockIdx]))698      : this.helper.getApi();699700    return (await api.query.testUtils.testValue()).toJSON();701  }702703  async justTakeFee(signer: TSigner) {704    await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.justTakeFee', [], true);705  }706707  async selfCancelingInc(signer: TSigner, scheduledId: string, maxTestVal: number) {708    await this.helper.executeExtrinsic(signer, 'api.tx.testUtils.selfCancelingInc', [scheduledId, maxTestVal], true);709  }710}711712class EventCapture {713  helper: DevUniqueHelper;714  eventSection: string;715  eventMethod: string;716  events: EventRecord[] = [];717  unsubscribe: VoidFn | null = null;718719  constructor(720    helper: DevUniqueHelper,721    eventSection: string,722    eventMethod: string,723  ) {724    this.helper = helper;725    this.eventSection = eventSection;726    this.eventMethod = eventMethod;727  }728729  async startCapture() {730    this.stopCapture();731    this.unsubscribe = (await this.helper.getApi().query.system.events((eventRecords: FrameSystemEventRecord[]) => {732      const newEvents = eventRecords.filter(r => {733        return r.event.section == this.eventSection && r.event.method == this.eventMethod;734      });735736      this.events.push(...newEvents);737    })) as any;738  }739740  stopCapture() {741    if (this.unsubscribe !== null) {742      this.unsubscribe();743    }744  }745746  extractCapturedEvents() {747    return this.events;748  }749}750751class AdminGroup {752  helper: UniqueHelper;753754  constructor(helper: UniqueHelper) {755    this.helper = helper;756  }757758  async payoutStakers(signer: IKeyringPair, stakersToPayout: number):  Promise<{staker: string, stake: bigint, payout: bigint}[]> {759    const payoutResult = await this.helper.executeExtrinsic(signer, 'api.tx.appPromotion.payoutStakers', [stakersToPayout], true);760    return payoutResult.result.events.filter(e => e.event.method === 'StakingRecalculation').map(e => {761      return {762        staker: e.event.data[0].toString(),763        stake: e.event.data[1].toBigInt(),764        payout: e.event.data[2].toBigInt(),765      };766    });767  }768}
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -18,7 +18,7 @@
 import {blake2AsHex} from '@polkadot/util-crypto';
 import config from '../config';
 import {XcmV2TraitsError} from '../interfaces';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingAstarPlaygrounds} from '../util';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
 
 const QUARTZ_CHAIN = 2095;
 const STATEMINE_CHAIN = 1000;
@@ -983,19 +983,28 @@
 
 describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
   let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
+  let sender: IKeyringPair;
+
+  // Quartz -> Shiden
+  const shidenInitialBalance = 1n * (10n ** 18n); // 1 SHD, existencial deposit required in order to perform XCM call
+  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
+  const qtzToShidenTransferred = 10n * (10n ** 18n); // 10 QTZ
+  const qtzToShidenArrived = 9_999_999_999_088_000_000n; // 9.999 ... QTZ, Shiden takes a commision in foreign tokens
+  const senderIinitialBalanceQTZ = 100n * (10n ** 18n); // How many QTZ sender has initially
+  const senderBalanceAfterXCM = 89_941967662676666465n; // 89.94... QTZ after XCM call
 
-  const shidenInitialBalance = 1n * (10n ** 18n);
-  const qtzToShidenAmount = 10n * (10n ** 18n);
+  // Shiden -> Quartz
+  const qtzFromShidenTransfered = 5n * (10n ** 18n); // 5 QTZ
+  const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 4.999_999_999_088_000_000n QTZ
 
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
       alice = await privateKey('//Alice');
-      [randomAccount] = await helper.arrange.createAccounts([100n], alice);
-      console.log('randomAccount', randomAccount.address);
+      [sender] = await helper.arrange.createAccounts([100n], alice);
+      console.log('sender', sender.address);
     });
 
-    await usingAstarPlaygrounds(shidenUrl, async (helper) => {
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
       console.log('1. Create foreign asset and metadata');
       await helper.assets.create(
         alice,
@@ -1027,12 +1036,10 @@
       await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, 1]);
 
       console.log('3. Set payment for computation');
-      // TODO this is Phala's price, what price will be for Unique?
-      const unitsPerSecond = 228_000_000_000n;
       await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
 
       console.log('4. Transfer 1 SDN to recepient');
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);
+      await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);
     });
   });
 
@@ -1055,7 +1062,7 @@
           X1: {
             AccountId32: {
               network: 'Any',
-              id: randomAccount.addressRaw,
+              id: sender.addressRaw,
             },
           },
         },
@@ -1072,34 +1079,42 @@
             },
           },
           fun: {
-            Fungible: qtzToShidenAmount,
+            Fungible: qtzToShidenTransferred,
           },
         },
       ],
     };
 
-    // Initial balance is 100 UNQ
-    expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(100n * (10n ** 18n));
+    // Initial balance is 100 QTZ
+    const balanceBefore = await helper.balance.getSubstrate(sender.address);
+    console.log(`Initial balance is: ${balanceBefore}`);
+    expect(balanceBefore).to.eq(senderIinitialBalanceQTZ);
 
     const feeAssetItem = 0;
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    await helper.xcm.limitedReserveTransferAssets(sender, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
 
     // Balance after reserve transfer is less than 90
-    expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(89_941967662676666465n);
+    const balanceAfter = await helper.balance.getSubstrate(sender.address);
+    console.log(`QTZ Balance on Quartz after XCM is: ${balanceAfter}`);
+    console.log(`Quartz's QTZ commission is: ${balanceBefore - balanceAfter}`);
+    expect(balanceAfter).to.eq(senderBalanceAfterXCM);
 
-    await usingAstarPlaygrounds(shidenUrl, async (helper) => {
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
       await helper.wait.newBlocks(3);
-      const xcUNQbalance = await helper.assets.account(1, randomAccount.address);
-      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);
+      const xcQTZbalance = await helper.assets.account(1, sender.address);
+      const shidenBalance = await helper.balance.getSubstrate(sender.address);
 
-      expect(xcUNQbalance).to.eq(9_999_999_999_088_000_000n);
-      // Astar balance does not changed
-      expect(astarBalance).to.eq(1_000_000_000_000_000_000n);
+      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.only('Should connect to Shiden and send QTZ back', async ({helper}) => {
-    await usingAstarPlaygrounds(shidenUrl, async (helper) => {
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
       const destination = {
         V1: {
           parents: 1,
@@ -1118,7 +1133,7 @@
             X1: {
               AccountId32: {
                 network: 'Any',
-                id: randomAccount.addressRaw,
+                id: sender.addressRaw,
               },
             },
           },
@@ -1139,30 +1154,35 @@
               },
             },
             fun: {
-              Fungible: 5_000_000_000_000_000_000n,
+              Fungible: qtzFromShidenTransfered,
             },
           },
         ],
       };
 
       // Initial balance is 1 SDN
-      expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(1_000_000_000_000_000_000n);
+      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;
-      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);
+      // 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 xcUNQbalance = await helper.assets.account(1, randomAccount.address);
-      const balanceSDN = await helper.balance.getSubstrate(randomAccount.address);
+      const xcQTZbalance = await helper.assets.account(1, sender.address);
+      const balanceSDN = await helper.balance.getSubstrate(sender.address);
+      console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);
 
-      // Assert: xcQTZ balance decreased
-      expect(xcUNQbalance).to.eq(4_999_999_999_088_000_000n);
+      // Assert: xcQTZ balance correctly decreased
+      expect(xcQTZbalance).to.eq(qtzOnShidenLeft);
       // Assert: SDN balance is 0.996...
       expect(balanceSDN / (10n ** 15n)).to.eq(996n);
     });
 
     await helper.wait.newBlocks(3);
-    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);
-    expect(balanceUNQ).to.eq(89_941967662676666465n + 5_000_000_000_000_000_000n);
+    const balanceQTZ = await helper.balance.getSubstrate(sender.address);
+    console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`);
+    expect(balanceQTZ).to.eq(senderBalanceAfterXCM + qtzFromShidenTransfered);
   });
 });
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -987,8 +987,17 @@
   let alice: IKeyringPair;
   let randomAccount: IKeyringPair;
 
-  const astarInitialBalance = 1n * (10n ** 18n);
-  const unqToAstarAmount = 10n * (10n ** 18n);
+  // Unique -> Astar
+  const astarInitialBalance = 1n * (10n ** 18n); // 1 ASTR. Existencial deposit required in order to perform XCM call
+  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
+  const unqToAstarTransferred = 10n * (10n ** 18n); // 10 UNQ
+  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Shiden takes a commision in foreign tokens
+  const senderIinitialBalanceUNQ = 100n * (10n ** 18n); // How many UNQ sender has initially
+  const senderBalanceAfterXCM = 89_941967662676666465n; // 89.94... UNQ after XCM call
+
+  // Astar -> Unique
+  const unqFromAstarTransfered = 5n * (10n ** 18n); // 5 UNQ
+  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ
 
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
@@ -1029,8 +1038,6 @@
       await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, 1]);
 
       console.log('3. Set payment for computation');
-      // TODO this is Phala's price, what price will be for Unique?
-      const unitsPerSecond = 228_000_000_000n;
       await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
 
       console.log('4. Transfer 1 ASTR to recepient');
@@ -1074,29 +1081,37 @@
             },
           },
           fun: {
-            Fungible: unqToAstarAmount,
+            Fungible: unqToAstarTransferred,
           },
         },
       ],
     };
 
     // Initial balance is 100 UNQ
-    expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(100n * (10n ** 18n));
+    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);
+    console.log(`Initial balance is: ${balanceBefore}`);
+    expect(balanceBefore).to.eq(senderIinitialBalanceUNQ);
 
     const feeAssetItem = 0;
     await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
 
     // Balance after reserve transfer is less than 90
-    expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(89_941967662676666465n);
+    const balanceAfter = await helper.balance.getSubstrate(randomAccount.address);
+    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfter}`);
+    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfter}`);
+    expect(balanceAfter).to.eq(senderBalanceAfterXCM);
 
     await usingAstarPlaygrounds(astarUrl, async (helper) => {
       await helper.wait.newBlocks(3);
       const xcUNQbalance = await helper.assets.account(1, randomAccount.address);
       const astarBalance = await helper.balance.getSubstrate(randomAccount.address);
 
-      expect(xcUNQbalance).to.eq(9_999_999_999_088_000_000n);
+      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(1_000_000_000_000_000_000n);
+      expect(astarBalance).to.eq(astarInitialBalance);
     });
   });
 
@@ -1141,30 +1156,35 @@
               },
             },
             fun: {
-              Fungible: 5_000_000_000_000_000_000n,
+              Fungible: unqFromAstarTransfered,
             },
           },
         ],
       };
 
       // Initial balance is 1 ASTR
-      expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(1_000_000_000_000_000_000n);
+      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(1, randomAccount.address);
       const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);
+      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
 
-      // Assert: xcUNQ balance decreased
-      expect(xcUNQbalance).to.eq(4_999_999_999_088_000_000n);
+      // Assert: xcUNQ balance correctly decreased
+      expect(xcUNQbalance).to.eq(unqOnAstarLeft);
       // Assert: ASTR balance is 0.996...
       expect(balanceAstar / (10n ** 15n)).to.eq(996n);
     });
 
     await helper.wait.newBlocks(3);
     const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);
-    expect(balanceUNQ).to.eq(89_941967662676666465n + 5_000_000_000_000_000_000n);
+    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);
+    expect(balanceUNQ).to.eq(senderBalanceAfterXCM + unqFromAstarTransfered);
   });
 
   itSub.skip('Should not accept limitedReserveTransfer of UNQ from ASTAR', async ({helper}) => {
@@ -1208,30 +1228,18 @@
               },
             },
             fun: {
-              Fungible: 5_000_000_000_000_000_000n, // TODO set another value
+              Fungible: unqFromAstarTransfered,
             },
           },
         ],
       };
 
       // Initial balance is 1 ASTAR
-      expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(1_000_000_000_000_000_000n);
+      expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(astarInitialBalance);
 
       const feeAssetItem = 0;
+      // TODO: expect rejected:
       await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-
-      // Balance after reserve transfer is less than 1 ASTAR
-      const xcUNQbalance = await helper.assets.account(1, randomAccount.address);
-      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);
-
-      // xcUNQ balance decreased
-      expect(xcUNQbalance).to.eq(4_999_999_999_088_000_000n);
-      // Astar balance is 0.997...
-      expect(balanceAstar / (10n ** 15n)).to.eq(997n);
     });
-
-    await helper.wait.newBlocks(3);
-    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);
-    expect(balanceUNQ).to.eq(89_941967662676666465n + 5_000_000_000_000_000_000n);
   });
 });