git.delta.rocks / unique-network / refs/commits / 61fc26e2c8d2

difftreelog

test(xcm qtz) added `XcmTestHelper`

PraetorP2023-09-28parent: #dc6f0e2.patch.diff
in: master

5 files changed

modifiedtests/package.jsondiffbeforeafterboth
after · tests/package.json
1{2  "name": "unique-tests",3  "version": "1.0.0",4  "description": "Unique Chain Tests",5  "main": "",6  "devDependencies": {7    "@polkadot/typegen": "10.9.1",8    "@types/chai": "^4.3.3",9    "@types/chai-as-promised": "^7.1.5",10    "@types/chai-like": "^1.1.1",11    "@types/chai-subset": "^1.3.3",12    "@types/mocha": "^10.0.0",13    "@types/node": "^20.4.2",14    "@typescript-eslint/eslint-plugin": "^6.0.0",15    "@typescript-eslint/parser": "^6.0.0",16    "chai": "^4.3.6",17    "chai-subset": "^1.6.0",18    "eslint": "^8.45.0",19    "eslint-plugin-mocha": "^10.1.0",20    "mocha": "^10.1.0",21    "mochawesome": "^7.1.3",22    "ts-node": "^10.9.1",23    "typescript": "^5.1.6"24  },25  "mocha": {26    "timeout": 9999999,27    "require": [28      "ts-node/register"29    ]30  },31  "scripts": {32    "lint": "eslint --ext .ts,.js src/",33    "fix": "yarn lint --fix",34    "setup": "ts-node --esm ./src/util/globalSetup.ts",35    "setIdentities": "ts-node --esm ./src/util/identitySetter.ts",36    "checkRelayIdentities": "ts-node --esm ./src/util/relayIdentitiesChecker.ts",37    "frankenstein": "ts-node --esm ./src/util/frankenstein.ts",38    "_test": "yarn setup && mocha --timeout 9999999 --loader=ts-node/esm.mjs",39    "_testParallel": "yarn setup && mocha --timeout 9999999 --parallel --loader=ts-node/esm.mjs",40    "test": "yarn _test './src/**/*.*test.ts'",41    "testParallelFull": "yarn testParallel && yarn testSequential",42    "testParallel": "yarn _testParallel './src/**/*.test.ts'",43    "testSequential": "yarn _test './src/**/*.seqtest.ts'",44    "testStructure": "yarn _test ./**/nesting/*.*test.ts",45    "testGovernance": "RUN_GOV_TESTS=1 yarn _test ./**/governance/*.*test.ts",46    "testEth": "yarn _test './**/eth/**/*.*test.ts'",47    "testEthNesting": "yarn _test './**/eth/nesting/**/*.*test.ts'",48    "testEthFractionalizer": "yarn _test './**/eth/fractionalizer/**/*.*test.ts'",49    "testEthMarketplace": "yarn _test './**/eth/marketplace/**/*.*test.ts'",50    "testEthMarket": "yarn _test './**/eth/marketplace-v2/**/*.*test.ts'",51    "testSub": "yarn _test './**/sub/**/*.*test.ts'",52    "testSubNesting": "yarn _test './**/sub/nesting/**/*.*test.ts'",53    "testEvent": "yarn _test ./src/check-event/*.*test.ts",54    "testEthPayable": "yarn _test './**/eth/payable.test.ts'",55    "testEvmCoder": "yarn _test './**/eth/evmCoder.test.ts'",56    "testNesting": "yarn _test ./**/nest.test.ts",57    "testUnnesting": "yarn _test ./**/unnest.test.ts",58    "testProperties": "yarn _test ./**/collectionProperties.*test.ts ./**/tokenProperties.*test.ts ./**/getPropertiesRpc.test.ts",59    "testCollectionProperties": "yarn _test ./**/collectionProperties.*test.ts",60    "testTokenProperties": "yarn _test ./**/tokenProperties.*test.ts",61    "testMigration": "yarn _test ./**/nesting/migration-check.test.ts",62    "testAddCollectionAdmin": "yarn _test ./**/addCollectionAdmin.test.ts",63    "testSetCollectionLimits": "yarn _test ./**/setCollectionLimits.test.ts",64    "testChangeCollectionOwner": "yarn _test ./**/change-collection-owner.test.ts",65    "testSetCollectionSponsor": "yarn _test ./**/setCollectionSponsor.test.ts",66    "testConfirmSponsorship": "yarn _test ./**/confirmSponsorship.test.ts",67    "testRemoveCollectionAdmin": "yarn _test ./**/removeCollectionAdmin.test.ts",68    "testRemoveCollectionSponsor": "yarn _test ./**/removeCollectionSponsor.test.ts",69    "testAllowLists": "yarn _test ./**/allowLists.test.ts",70    "testConnection": "yarn _test ./**/connection.test.ts",71    "testContracts": "yarn _test ./**/contracts.test.ts",72    "testCreateItem": "yarn _test ./**/createItem.test.ts",73    "testCreateMultipleItems": "yarn _test ./**/createMultipleItems.test.ts",74    "testCreateMultipleItemsEx": "yarn _test ./**/createMultipleItemsEx.test.ts",75    "testApprove": "yarn _test ./**/approve.test.ts",76    "testTransferFrom": "yarn _test ./**/transferFrom.test.ts",77    "testCreateCollection": "yarn _test ./**/createCollection.test.ts",78    "testDestroyCollection": "yarn _test ./**/destroyCollection.test.ts",79    "testToggleContractAllowList": "yarn _test ./**/toggleContractAllowList.test.ts",80    "testAddToContractAllowList": "yarn _test ./**/addToContractAllowList.test.ts",81    "testTransfer": "yarn _test ./**/transfer.test.ts",82    "testBurnItem": "yarn _test ./**/burnItem.test.ts",83    "testAdminTransferAndBurn": "yarn _test ./**/adminTransferAndBurn.test.ts",84    "testSetPermissions": "yarn _test ./**/setPermissions.test.ts",85    "testCreditFeesToTreasury": "yarn _test ./**/creditFeesToTreasury.seqtest.ts",86    "testContractSponsoring": "yarn _test ./**/eth/contractSponsoring.test.ts",87    "testEnableContractSponsoring": "yarn _test ./**/enableContractSponsoring.test.ts",88    "testRemoveFromContractAllowList": "yarn _test ./**/removeFromContractAllowList.test.ts",89    "testSetContractSponsoringRateLimit": "yarn _test ./**/setContractSponsoringRateLimit.test.ts",90    "testSetOffchainSchema": "yarn _test ./**/setOffchainSchema.test.ts",91    "testNextSponsoring": "yarn _test ./**/nextSponsoring.test.ts",92    "testOverflow": "yarn _test ./**/overflow.test.ts",93    "testMaintenance": "yarn _test ./**/maintenance.seqtest.ts",94    "testInflation": "yarn _test ./**/inflation.seqtest.ts",95    "testScheduler": "yarn _test ./**/scheduler.seqtest.ts",96    "testSchedulingEVM": "yarn _test ./**/eth/scheduling.test.ts",97    "testPalletPresence": "yarn _test ./**/pallet-presence.test.ts",98    "testEnableDisableTransfers": "yarn _test ./**/enableDisableTransfer.test.ts",99    "testLimits": "yarn _test ./**/limits.test.ts",100    "testEthCreateNFTCollection": "yarn _test ./**/eth/createNFTCollection.test.ts",101    "testEthCreateRFTCollection": "yarn _test ./**/eth/createRFTCollection.test.ts",102    "testEthNFT": "yarn _test ./**/eth/nonFungible.test.ts",103    "testRFT": "yarn _test ./**/refungible.test.ts",104    "testEthRFT": "yarn _test ./**/eth/reFungible.test.ts ./**/eth/reFungibleToken.test.ts",105    "testFT": "yarn _test ./**/fungible.test.ts",106    "testEthFT": "yarn _test ./**/eth/fungible.test.ts",107    "testRPC": "yarn _test ./**/rpc.test.ts",108    "testPromotion": "yarn _test ./**/appPromotion/*test.ts",109    "testApiConsts": "yarn _test ./**/apiConsts.test.ts",110    "testCouncil": "yarn _test ./**/council.*test.ts",111    "testDemocracy": "yarn _test ./**/democracy.*test.ts",112    "testCollators": "RUN_COLLATOR_TESTS=1 yarn _test ./**/collator-selection/**.*test.ts --timeout 49999999",113    "testCollatorSelection": "RUN_COLLATOR_TESTS=1 yarn _test ./**/collatorSelection.*test.ts --timeout 49999999",114    "testIdentity": "RUN_COLLATOR_TESTS=1 yarn _test ./**/identity.*test.ts --timeout 49999999",115    "testLowLevelXcmUnique": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/lowLevelXcmUnique.test.ts",116    "testXcmUnique": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/xcmUnique.test.ts",117    "testFullXcmUnique": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/*Unique.test.ts",118    "testXcmQuartz": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/xcmQuartz.test.ts",119    "testLowLevelXcmQuartz": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/lowLevelXcmQuartz.test.ts",120    "testFullXcmQuartz": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/*Quartz.test.ts",121    "testXcmOpal": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/xcmOpal.test.ts",122    "testXcmTransferAcala": "yarn _test ./**/xcm/xcmTransferAcala.test.ts acalaId=2000 uniqueId=5000",123    "testXcmTransferStatemine": "yarn _test ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000",124    "testXcmTransferMoonbeam": "yarn _test ./**/xcm/xcmTransferMoonbeam.test.ts",125    "benchMintingFee": "ts-node src/benchmarks/mintFee/benchmark.ts",126    "load": "yarn _test './**/*.load.ts'",127    "loadTransfer": "ts-node src/transfer.nload.ts",128    "polkadot-types-fetch-metadata": "yarn ts-node --esm src/fetchMetadata.ts",129    "polkadot-types-from-defs": "ts-node --esm ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .",130    "polkadot-types-from-chain": "ts-node --esm ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",131    "polkadot-types": "echo \"export default {}\" > src/interfaces/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain",132    "generateEnv": "ts-node --esm ./src/generateEnv.ts"133  },134  "author": "",135  "license": "SEE LICENSE IN ../LICENSE",136  "homepage": "",137  "dependencies": {138    "@openzeppelin/contracts": "^4.9.2",139    "@polkadot/api": "10.9.1",140    "@polkadot/rpc-core": "^10.9.1",141    "@polkadot/util": "12.3.2",142    "@polkadot/util-crypto": "12.3.2",143    "@polkadot/wasm-crypto-asmjs": "^7.2.1",144    "@polkadot/wasm-crypto-wasm": "^7.2.1",145    "@rmrk-team/evm-contracts": "^1.2.1",146    "@typechain/web3-v1": "^6.0.3",147    "chai-as-promised": "^7.1.1",148    "chai-like": "^1.1.1",149    "csv-writer": "^1.6.0",150    "find-process": "^1.4.7",151    "lossless-json": "^2.0.9",152    "solc": "0.8.20",153    "typechain": "^8.2.0",154    "web3": "1.10.0"155  },156  "resolutions": {157    "decode-uri-component": "^0.2.1"158  },159  "type": "module",160  "packageManager": "yarn@3.6.1"161}
addedtests/src/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/xcm/lowLevelXcmQuartz.test.ts
@@ -0,0 +1,381 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds,  usingMoonriverPlaygrounds, usingShidenPlaygrounds} from '../util';
+import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
+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, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT} from './xcm.types';
+
+
+const testHelper = new XcmTestHelper('quartz');
+
+describeXCM('[XCMLL] 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,
+      };
+
+      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+      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 () => {
+    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;
+  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();
+    });
+  });
+
+  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,
+  };
+
+  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;
+
+      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 () => {
+    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.reserveTransferUNQfrom('moonriver', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {
+  let alice: IKeyringPair;
+  let sender: IKeyringPair;
+
+  const QTZ_ASSET_ID_ON_SHIDEN = 1;
+  const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n;
+
+  // Quartz -> Shiden
+  const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
+  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
+  const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ
+  const qtzToShidenArrived = 9_999_999_999_088_000_000n; // 9.999 ... QTZ, Shiden takes a commision in foreign tokens
+
+  // Shiden -> Quartz
+  const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ
+  const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 4.999_999_999_088_000_000n 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) => {
+      console.log('1. Create foreign asset and metadata');
+      // TODO update metadata with values from production
+      await helper.assets.create(
+        alice,
+        QTZ_ASSET_ID_ON_SHIDEN,
+        alice.address,
+        QTZ_MINIMAL_BALANCE_ON_SHIDEN,
+      );
+
+      await helper.assets.setMetadata(
+        alice,
+        QTZ_ASSET_ID_ON_SHIDEN,
+        'Cross chain QTZ',
+        'xcQTZ',
+        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]);
+
+      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 () => {
+    await testHelper.sendUnqTo('shiden', sender);
+  });
+
+  itSub('Should connect to Shiden and send QTZ back', async () => {
+    await testHelper.sendUnqBack('shiden', alice, sender);
+  });
+
+  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.reserveTransferUNQfrom('shiden', alice);
+  });
+});
modifiedtests/src/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/lowLevelXcmUnique.test.ts
+++ b/tests/src/xcm/lowLevelXcmUnique.test.ts
@@ -20,7 +20,7 @@
 import {Event} from '../util/playgrounds/unique.dev';
 import {nToBigInt} from '@polkadot/util';
 import {hexToString} from '@polkadot/util';
-import {ASTAR_DECIMALS, NETWORKS, SAFE_XCM_VERSION, UNIQUE_CHAIN, UNQ_DECIMALS, acalaUrl, astarUrl, expectFailedToTransact, expectUntrustedReserveLocationFail, getDevPlayground, mapToChainId, mapToChainUrl, maxWaitBlocks, moonbeamUrl, polkadexUrl, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types';
+import {ASTAR_DECIMALS, NETWORKS, SAFE_XCM_VERSION, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, expectFailedToTransact, expectUntrustedReserveLocationFail, getDevPlayground, mapToChainId, mapToChainUrl, maxWaitBlocks, moonbeamUrl, polkadexUrl, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types';
 
 
 const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;
@@ -34,6 +34,7 @@
 let balanceUniqueTokenFinal: bigint;
 let unqFees: bigint;
 
+const testHelper = new XcmTestHelper('unique');
 
 async function genericSendUnqTo(
   networkName: keyof typeof NETWORKS,
@@ -434,20 +435,20 @@
   });
 
   itSub('Should connect and send UNQ to Polkadex', async () => {
-    await genericSendUnqTo('polkadex', randomAccount);
+    await testHelper.sendUnqTo('polkadex', randomAccount);
   });
 
 
   itSub('Should connect to Polkadex and send UNQ back', async () => {
-    await genericSendUnqBack('polkadex', alice, randomAccount);
+    await testHelper.sendUnqBack('polkadex', alice, randomAccount);
   });
 
   itSub('Polkadex can send only up to its balance', async () => {
-    await genericSendOnlyOwnedBalance('polkadex', alice);
+    await testHelper.sendOnlyOwnedBalance('polkadex', alice);
   });
 
   itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {
-    await genericReserveTransferUNQfrom('polkadex', alice);
+    await testHelper.reserveTransferUNQfrom('polkadex', alice);
   });
 });
 
@@ -574,19 +575,19 @@
   });
 
   itSub('Should connect and send UNQ to Moonbeam', async () => {
-    await genericSendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);
+    await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);
   });
 
   itSub('Should connect to Moonbeam and send UNQ back', async () => {
-    await genericSendUnqBack('moonbeam', alice, randomAccountUnique);
+    await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique);
   });
 
   itSub('Moonbeam can send only up to its balance', async () => {
-    await genericSendOnlyOwnedBalance('moonbeam', alice);
+    await testHelper.sendOnlyOwnedBalance('moonbeam', alice);
   });
 
   itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {
-    await genericReserveTransferUNQfrom('moonbeam', alice);
+    await testHelper.reserveTransferUNQfrom('moonbeam', alice);
   });
 });
 
modifiedtests/src/xcm/xcm.types.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcm.types.ts
+++ b/tests/src/xcm/xcm.types.ts
@@ -1,4 +1,5 @@
-import {usingAcalaPlaygrounds, usingAstarPlaygrounds, usingMoonbeamPlaygrounds, usingPolkadexPlaygrounds} from '../util';
+import {IKeyringPair} from '@polkadot/types/types';
+import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingShidenPlaygrounds} from '../util';
 import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
 import config from '../config';
 
@@ -9,19 +10,39 @@
 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 maxWaitBlocks = 6;
 
+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: {
@@ -52,9 +73,15 @@
   astar: usingAstarPlaygrounds,
   polkadex: usingPolkadexPlaygrounds,
   moonbeam: usingMoonbeamPlaygrounds,
+  moonriver: usingMoonriverPlaygrounds,
+  karura: usingKaruraPlaygrounds,
+  shiden: usingShidenPlaygrounds,
 } as const;
+type NetworkNames = keyof typeof NETWORKS;
 
-export function mapToChainId(networkName: keyof typeof NETWORKS) {
+type NativeRuntime = 'opal' | 'quartz' | 'unique';
+
+export function mapToChainId(networkName: keyof typeof NETWORKS): number {
   switch (networkName) {
     case 'acala':
       return ACALA_CHAIN;
@@ -64,10 +91,16 @@
       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: keyof typeof NETWORKS): string {
+export function mapToChainUrl(networkName: NetworkNames): string {
   switch (networkName) {
     case 'acala':
       return acalaUrl;
@@ -77,9 +110,364 @@
       return moonbeamUrl;
     case 'polkadex':
       return polkadexUrl;
+    case 'moonriver':
+      return moonriverUrl;
+    case 'karura':
+      return karuraUrl;
+    case 'shiden':
+      return shidenUrl;
   }
 }
 
-export function getDevPlayground<T extends keyof typeof NETWORKS>(name: T) {
+export function getDevPlayground(name: NetworkNames) {
   return NETWORKS[name];
+}
+
+export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;
+const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
+const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
+const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;
+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 10;
+      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;
+    }
+  }
+
+  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('[Unique -> %s] transaction fees on Unique: %s UNQ', networkName, helper.util.bigIntToDecimals(this._unqFees));
+      expect(this._unqFees > 0n, 'Negative fees UNQ, 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, uniqueVersionedMultilocation, xcmProgram);
+          xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        } else if('fastDemocracy' in helper) {
+          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, 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, uniqueVersionedMultilocation, maliciousXcmProgram);
+          maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        } else if('fastDemocracy' in 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(`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 reserveTransferUNQfrom(netwokrName: keyof typeof NETWORKS, sudoer: IKeyringPair) {
+    const networkUrl = mapToChainUrl(netwokrName);
+    const targetPlayground = getDevPlayground(netwokrName);
+
+    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, uniqueVersionedMultilocation, 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', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);
+          // Needed to bypass the call filter.
+          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+          await helper.fastDemocracy.executeProposal(`${netwokrName} 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, uniqueVersionedMultilocation, maliciousXcmProgramHereId);
+          maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+        else if('fastDemocracy' in 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(`${netwokrName} 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, uniqueVersionedMultilocation, maliciousXcmProgramFullId);
+          messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        } else if('fastDemocracy' in 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(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);
+
+          messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+      });
+      await expectFailedToTransact(helper, messageSent);
+    });
+  }
+
 }
\ No newline at end of file
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -15,29 +15,13 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 import {IKeyringPair} from '@polkadot/types/types';
-import config from '../config';
 import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
 import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
+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';
+
 
-const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095);
-const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000);
-const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000);
-const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023);
-const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007);
 
 const STATEMINE_PALLET_INSTANCE = 50;
-
-const relayUrl = config.relayUrl;
-const statemineUrl = config.statemineUrl;
-const karuraUrl = config.karuraUrl;
-const moonriverUrl = config.moonriverUrl;
-const shidenUrl = config.shidenUrl;
-
-const RELAY_DECIMALS = 12;
-const STATEMINE_DECIMALS = 12;
-const KARURA_DECIMALS = 12;
-const SHIDEN_DECIMALS = 18n;
-const QTZ_DECIMALS = 18n;
 
 const TRANSFER_AMOUNT = 2000000000000000000000000n;