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

difftreelog

feat(ci) xcm tests for hydraDx

PraetorP2023-12-08parent: #78e23a0.patch.diff
in: master

14 files changed

modified.baedeker/rewrites.example.jsonnetdiffbeforeafterboth
--- a/.baedeker/rewrites.example.jsonnet
+++ b/.baedeker/rewrites.example.jsonnet
@@ -1,17 +1,18 @@
 local dotenv = {
-    [std.splitLimit(line, "=", 2)[0]]: std.splitLimit(line, "=", 2)[1]
-    for line in std.split(importstr "../.env", "\n")
-    if line != ""
-    if std.member(line, "=")
+  [std.splitLimit(line, '=', 2)[0]]: std.splitLimit(line, '=', 2)[1]
+  for line in std.split(importstr '../.env', '\n')
+  if line != ''
+  if std.member(line, '=')
 };
 
 function(prev, repoDir)
-(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({
-	'bin/unique':'%s/target/release/unique-collator' % repoDir,
-	'bin/polkadot':{dockerImage:'uniquenetwork/builder-polkadot:%s' % dotenv.POLKADOT_MAINNET_BRANCH},
-	'bin/acala':{dockerImage:'uniquenetwork/builder-acala:%s' % dotenv.ACALA_BUILD_BRANCH},
-	'bin/moonbeam':{dockerImage:'uniquenetwork/builder-moonbeam:%s' % dotenv.MOONBEAM_BUILD_BRANCH},
-	'bin/cumulus':{dockerImage:'uniquenetwork/builder-cumulus:%s' % dotenv.STATEMINE_BUILD_BRANCH},
-	'bin/astar':{dockerImage:'uniquenetwork/builder-astar:%s' % dotenv.ASTAR_BUILD_BRANCH},
-	'bin/polkadex':{dockerImage:'uniquenetwork/builder-polkadex:%s' % dotenv.POLKADEX_BUILD_BRANCH},
-})(prev)
+  (import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({
+    'bin/unique': '%s/target/release/unique-collator' % repoDir,
+    'bin/polkadot': { dockerImage: 'uniquenetwork/builder-polkadot:%s' % dotenv.POLKADOT_MAINNET_BRANCH },
+    'bin/acala': { dockerImage: 'uniquenetwork/builder-acala:%s' % dotenv.ACALA_BUILD_BRANCH },
+    'bin/moonbeam': { dockerImage: 'uniquenetwork/builder-moonbeam:%s' % dotenv.MOONBEAM_BUILD_BRANCH },
+    'bin/cumulus': { dockerImage: 'uniquenetwork/builder-cumulus:%s' % dotenv.STATEMINE_BUILD_BRANCH },
+    'bin/astar': { dockerImage: 'uniquenetwork/builder-astar:%s' % dotenv.ASTAR_BUILD_BRANCH },
+    'bin/polkadex': { dockerImage: 'uniquenetwork/builder-polkadex:%s' % dotenv.POLKADEX_BUILD_BRANCH },
+    'bin/hydradx': { dockerImage: 'uniquenetwork/builder-hydraDx:%s' % dotenv.HYDRADX_BUILD_BRANCH },
+  })(prev)
modified.baedeker/xcm-opal.jsonnetdiffbeforeafterboth
--- a/.baedeker/xcm-opal.jsonnet
+++ b/.baedeker/xcm-opal.jsonnet
@@ -19,6 +19,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'relay',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob', 'charlie', 'dave', 'eve']
 	},
@@ -35,6 +38,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -52,6 +58,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
modified.baedeker/xcm-quartz.jsonnetdiffbeforeafterboth
--- a/.baedeker/xcm-quartz.jsonnet
+++ b/.baedeker/xcm-quartz.jsonnet
@@ -24,6 +24,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'relay',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob', 'charlie', 'dave', 'eve', 'ferdie']
 	},
@@ -40,6 +43,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -60,6 +66,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -79,6 +88,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para-nimbus',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alith', 'baltathar']
 	},
@@ -96,6 +108,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -113,6 +128,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
modified.baedeker/xcm-unique.jsonnetdiffbeforeafterboth
--- a/.baedeker/xcm-unique.jsonnet
+++ b/.baedeker/xcm-unique.jsonnet
@@ -25,8 +25,11 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'relay',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
-		for name in ['alice', 'bob', 'charlie', 'dave', 'eve', 'ferdie', 'gregory']
+		for name in ['alice', 'bob', 'charlie', 'dave', 'eve', 'ferdie', 'gregory', 'holly']
 	},
 };
 
@@ -41,6 +44,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -61,6 +67,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -80,6 +89,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para-nimbus',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alith', 'baltathar']
 	},
@@ -97,6 +109,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para-ed',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -114,6 +129,9 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
@@ -131,14 +149,39 @@
 		[name]: {
 			bin: $.bin,
 			wantedKeys: 'para',
+			extraArgs: [
+				"-lxcm=trace"
+			],
 		},
 		for name in ['alice', 'bob']
 	},
 };
 
+local hydraDx = {
+	name: 'hydraDx',
+	bin: 'bin/hydradx',
+	paraId: 1007,
+	spec: {Genesis:{
+		chain: 'local',
+		modify:: m.genericPara($),
+	}},
+	nodes: {
+		[name]: {
+			bin: $.bin,
+			wantedKeys: 'para',
+			legacyRpc: true,
+			extraArgs: [
+				"-lxcm=trace"
+			],
+		},
+		for name in ['alice', 'bob']
+	},
+};
+
+
 relay + {
 	parachains: {
 		[para.name]: para,
-		for para in [unique, acala, moonbeam, statemint, astar, polkadex]
+		for para in [unique, acala, moonbeam, statemint, astar, polkadex, hydraDx]
 	},
 }
modified.envdiffbeforeafterboth
--- a/.env
+++ b/.env
@@ -12,7 +12,7 @@
 UNIQUE_MAINNET_BRANCH=release-v10010063
 UNIQUE_REPLICA_FROM=wss://ws.unique.network:443
 POLKADEX_BUILD_BRANCH=v1.1.0
-
+HYDRADX_BUILD_BRANCH=v20.1.1
 
 KUSAMA_MAINNET_BRANCH=release-v1.0.0
 STATEMINE_BUILD_BRANCH=release-parachains-v9430
modified.vscode/settings.jsondiffbeforeafterboth
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -10,5 +10,5 @@
 		"SKIP_WASM_BUILD": "1"
 	},
 	"rust-analyzer.cargo.features": ["runtime-benchmarks", "try-runtime"],
-	"solidity.packageDefaultDependenciesDirectory": "tests/node_modules"
+	"solidity.packageDefaultDependenciesDirectory": "js-packages/node_modules"
 }
modifiedjs-packages/.vscode/settings.jsondiffbeforeafterboth
--- a/js-packages/.vscode/settings.json
+++ b/js-packages/.vscode/settings.json
@@ -3,7 +3,7 @@
 		"RUN_GOV_TESTS": "1",
 		"RUN_XCM_TESTS": "1"
 	},
-	"mochaExplorer.files": "src/**/*.test.ts",
+	"mochaExplorer.files": "tests/**/*.test.ts",
 	"mochaExplorer.require": "ts-node/register",
 	"eslint.format.enable": true,
 	"[javascript]": {
modifiedjs-packages/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/unique.dev.ts
+++ b/js-packages/playgrounds/unique.dev.ts
@@ -17,7 +17,7 @@
 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 {AcalaHelper, AstarHelper, MoonbeamHelper, PolkadexHelper, RelayHelper, WestmintHelper, ForeignAssetsGroup, XcmGroup, XTokensGroup, TokensGroup, HydraDxHelper} from './unique.xcm.js';
 import {CollectiveGroup, CollectiveMembershipGroup, DemocracyGroup, RankedCollectiveGroup, ReferendaGroup} from './unique.governance.js';
 import type {ICollectiveGroup, IFellowshipGroup} from './unique.governance.js';
 
@@ -667,6 +667,20 @@
   }
 }
 
+export class DevHydraDxHelper extends HydraDxHelper {
+  wait: WaitGroup;
+  fastDemocracy: HydraFastDemocracyGroup;
+
+  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+    options.helperBase = options.helperBase ?? DevHydraDxHelper;
+
+    super(logger, options);
+
+    this.wait = new WaitGroup(this);
+    this.fastDemocracy = new HydraFastDemocracyGroup(this);
+  }
+}
+
 export class DevKaruraHelper extends DevAcalaHelper {}
 
 export class ArrangeGroup {
@@ -1150,6 +1164,105 @@
   }
 }
 
+class HydraFastDemocracyGroup {
+  helper: DevHydraDxHelper;
+
+  constructor(helper: DevHydraDxHelper) {
+    this.helper = helper;
+  }
+
+  async executeProposal(proposalDesciption: string, encodedProposal: string) {
+    const proposalHash = blake2AsHex(encodedProposal);
+    const aliceAccount = this.helper.util.fromSeed('//Alice');
+    const bobAccount = this.helper.util.fromSeed('//Bob');
+    const eveAccount = this.helper.util.fromSeed('//Eve');
+
+    const councilVotingThreshold = 1;
+    const technicalCommitteeThreshold = 3;
+    const fastTrackVotingPeriod = 3;
+    const fastTrackDelayPeriod = 0;
+
+    console.log(`[democracy] executing '${proposalDesciption}' proposal`);
+
+    // >>> Propose external motion through council >>>
+    console.log('\t* Propose external motion through council.......');
+    const externalMotion = this.helper.democracy.externalProposeMajority({Inline: encodedProposal});
+    const encodedMotion = externalMotion?.method.toHex() || '';
+    const motionHash = blake2AsHex(encodedMotion);
+    console.log('\t* Motion hash is %s', motionHash);
+
+    await this.helper.collective.council.propose(
+      aliceAccount,
+      councilVotingThreshold,
+      externalMotion,
+      externalMotion.encodedLength,
+    );
+
+    // const councilProposalIdx = await this.helper.collective.council.proposalCount() - 1;
+    // await this.helper.collective.council.vote(aliceAccount, motionHash, councilProposalIdx, true);
+
+
+    // await this.helper.collective.council.close(
+    //   aliceAccount,
+    //   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(aliceAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);
+
+    const techProposalIdx = await this.helper.collective.techCommittee.proposalCount() - 1;
+    await this.helper.collective.techCommittee.vote(aliceAccount, fastTrackHash, techProposalIdx, true);
+    await this.helper.collective.techCommittee.vote(bobAccount, fastTrackHash, techProposalIdx, true);
+    await this.helper.collective.techCommittee.vote(eveAccount, fastTrackHash, techProposalIdx, true);
+
+    await this.helper.collective.techCommittee.close(
+      bobAccount,
+      fastTrackHash,
+      techProposalIdx,
+      {
+        refTime: 1_000_000_000,
+        proofSize: 1_000_000,
+      },
+      fastTrack.encodedLength,
+    );
+    console.log('\t* Fast track proposal through technical committee.......DONE');
+    // <<< Fast track proposal through technical committee <<<
+
+    const democracyStarted = await this.helper.wait.expectEvent(3, Event.Democracy.Started);
+    const referendumIndex = democracyStarted.referendumIndex;
+
+    // >>> Referendum voting >>>
+    console.log(`\t* Referendum #${referendumIndex} voting.......`);
+    await this.helper.democracy.referendumVote(eveAccount, referendumIndex, {
+      balance: 10_000_000_000_000_000_000n,
+      vote: {aye: true, conviction: 1},
+    });
+    console.log(`\t* Referendum #${referendumIndex} voting.......DONE`);
+    // <<< Referendum voting <<<
+
+    // Wait the proposal to pass
+    await this.helper.wait.expectEvent(3, Event.Democracy.Passed, event => event.referendumIndex == referendumIndex);
+
+    await this.helper.wait.newBlocks(1);
+
+    console.log(`[democracy] executing '${proposalDesciption}' proposal.......DONE`);
+  }
+}
+
 class WaitGroup {
   helper: ChainHelperBase;
 
modifiedjs-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth
before · js-packages/playgrounds/unique.xcm.ts
1import {ApiPromise, WsProvider} from '@polkadot/api';2import type {IKeyringPair} from '@polkadot/types/types';3import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js';4import type {ILogger, TSigner, TSubstrateAccount} from './types.js';5import type {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.xcm.js';678export class XcmChainHelper extends ChainHelperBase {9  override async connect(wsEndpoint: string, _listeners?: any): Promise<void> {10    const wsProvider = new WsProvider(wsEndpoint);11    this.api = new ApiPromise({12      provider: wsProvider,13    });14    await this.api.isReadyOrError;15    this.network = await UniqueHelper.detectNetwork(this.api);16  }17}1819class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {20  async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {21    await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);22  }23}2425class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {26  makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {27    const apiPrefix = 'api.tx.assetManager.';2829    const registerTx = this.helper.constructApiCall(30      apiPrefix + 'registerForeignAsset',31      [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],32    );3334    const setUnitsTx = this.helper.constructApiCall(35      apiPrefix + 'setAssetUnitsPerSecond',36      [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],37    );3839    const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);40    const encodedProposal = batchCall?.method.toHex() || '';41    return encodedProposal;42  }4344  async assetTypeId(location: any) {45    return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);46  }47}4849class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {50  notePreimagePallet: string;5152  constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {53    super(helper);54    this.notePreimagePallet = options.notePreimagePallet;55  }5657  async notePreimage(signer: TSigner, encodedProposal: string) {58    await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);59  }6061  externalProposeMajority(proposal: any) {62    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);63  }6465  fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {66    return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);67  }6869  async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {70    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);71  }72}7374class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {75  collective: string;7677  constructor(helper: MoonbeamHelper, collective: string) {78    super(helper);7980    this.collective = collective;81  }8283  async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {84    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);85  }8687  async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {88    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);89  }9091  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {92    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);93  }9495  async proposalCount() {96    return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));97  }98}99100class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {101  async whitelistToken(signer: TSigner, assetId: any) {102    await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);103  }104}105106export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {107  async register(signer: TSigner, assetId: any, name: string, tokenPrefix: string, mode: 'NFT' | { Fungible: number }) {108    await this.helper.executeExtrinsic(109      signer,110      'api.tx.foreignAssets.forceRegisterForeignAsset',111      [{V3: assetId}, this.helper.util.str2vec(name), tokenPrefix, mode],112      true,113    );114  }115116  async foreignCollectionId(assetId: any) {117    return (await this.helper.callRpc('api.query.foreignAssets.foreignAssetToCollection', [assetId])).toJSON();118  }119}120121export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {122  palletName: string;123124  constructor(helper: T, palletName: string) {125    super(helper);126127    this.palletName = palletName;128  }129130  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {131    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);132  }133134  async setSafeXcmVersion(signer: TSigner, version: number) {135    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);136  }137138  async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {139    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);140  }141142  async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {143    const destinationContent = {144      parents: 0,145      interior: {146        X1: {147          Parachain: destinationParaId,148        },149      },150    };151152    const beneficiaryContent = {153      parents: 0,154      interior: {155        X1: {156          AccountId32: {157            network: 'Any',158            id: targetAccount,159          },160        },161      },162    };163164    const assetsContent = [165      {166        id: {167          Concrete: {168            parents: 0,169            interior: 'Here',170          },171        },172        fun: {173          Fungible: amount,174        },175      },176    ];177178    let destination;179    let beneficiary;180    let assets;181182    if(xcmVersion == 2) {183      destination = {V1: destinationContent};184      beneficiary = {V1: beneficiaryContent};185      assets = {V1: assetsContent};186187    } else if(xcmVersion == 3) {188      destination = {V2: destinationContent};189      beneficiary = {V2: beneficiaryContent};190      assets = {V2: assetsContent};191192    } else {193      throw Error('Unknown XCM version: ' + xcmVersion);194    }195196    const feeAssetItem = 0;197198    await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);199  }200201  async send(signer: IKeyringPair, destination: any, message: any) {202    await this.helper.executeExtrinsic(203      signer,204      `api.tx.${this.palletName}.send`,205      [206        destination,207        message,208      ],209      true,210    );211  }212}213214export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {215  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {216    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);217  }218219  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {220    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);221  }222223  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {224    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);225  }226}227228229230export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {231  async accounts(address: string, currencyId: any) {232    const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;233    return BigInt(free);234  }235}236237export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {238  async create(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint) {239    await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);240  }241242  async forceCreate(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint, isSufficient = true) {243    await this.helper.executeExtrinsic(signer, 'api.tx.assets.forceCreate', [assetId, admin, isSufficient, minimalBalance], true);244  }245246  async setMetadata(signer: TSigner, assetId: number | bigint, name: string, symbol: string, decimals: number) {247    await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);248  }249250  async mint(signer: TSigner, assetId: number | bigint, beneficiary: string, amount: bigint) {251    await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);252  }253254  async assetInfo(assetId: number | bigint) {255    return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();256  }257258  async account(assetId: string | number | bigint, address: string) {259    const accountAsset = (260      await this.helper.callRpc('api.query.assets.account', [assetId, address])261    ).toJSON()! as any;262263    if(accountAsset !== null) {264      return BigInt(accountAsset['balance']);265    } else {266      return null;267    }268  }269}270271export class RelayHelper extends XcmChainHelper {272  balance: SubstrateBalanceGroup<RelayHelper>;273  xcm: XcmGroup<RelayHelper>;274275  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {276    super(logger, options.helperBase ?? RelayHelper);277278    this.balance = new SubstrateBalanceGroup(this);279    this.xcm = new XcmGroup(this, 'xcmPallet');280  }281}282283export class WestmintHelper extends XcmChainHelper {284  balance: SubstrateBalanceGroup<WestmintHelper>;285  xcm: XcmGroup<WestmintHelper>;286  assets: AssetsGroup<WestmintHelper>;287  xTokens: XTokensGroup<WestmintHelper>;288289  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {290    super(logger, options.helperBase ?? WestmintHelper);291292    this.balance = new SubstrateBalanceGroup(this);293    this.xcm = new XcmGroup(this, 'polkadotXcm');294    this.assets = new AssetsGroup(this);295    this.xTokens = new XTokensGroup(this);296  }297}298299export class MoonbeamHelper extends XcmChainHelper {300  balance: EthereumBalanceGroup<MoonbeamHelper>;301  assetManager: MoonbeamAssetManagerGroup;302  assets: AssetsGroup<MoonbeamHelper>;303  xTokens: XTokensGroup<MoonbeamHelper>;304  democracy: MoonbeamDemocracyGroup;305  collective: {306    council: MoonbeamCollectiveGroup,307    techCommittee: MoonbeamCollectiveGroup,308  };309310  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {311    super(logger, options.helperBase ?? MoonbeamHelper);312313    this.balance = new EthereumBalanceGroup(this);314    this.assetManager = new MoonbeamAssetManagerGroup(this);315    this.assets = new AssetsGroup(this);316    this.xTokens = new XTokensGroup(this);317    this.democracy = new MoonbeamDemocracyGroup(this, options);318    this.collective = {319      council: new MoonbeamCollectiveGroup(this, 'councilCollective'),320      techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),321    };322  }323}324325export class AstarHelper extends XcmChainHelper {326  balance: SubstrateBalanceGroup<AstarHelper>;327  assets: AssetsGroup<AstarHelper>;328  xcm: XcmGroup<AstarHelper>;329330  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {331    super(logger, options.helperBase ?? AstarHelper);332333    this.balance = new SubstrateBalanceGroup(this);334    this.assets = new AssetsGroup(this);335    this.xcm = new XcmGroup(this, 'polkadotXcm');336  }337}338339export class AcalaHelper extends XcmChainHelper {340  balance: SubstrateBalanceGroup<AcalaHelper>;341  assetRegistry: AcalaAssetRegistryGroup;342  xTokens: XTokensGroup<AcalaHelper>;343  tokens: TokensGroup<AcalaHelper>;344  xcm: XcmGroup<AcalaHelper>;345346  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {347    super(logger, options.helperBase ?? AcalaHelper);348349    this.balance = new SubstrateBalanceGroup(this);350    this.assetRegistry = new AcalaAssetRegistryGroup(this);351    this.xTokens = new XTokensGroup(this);352    this.tokens = new TokensGroup(this);353    this.xcm = new XcmGroup(this, 'polkadotXcm');354  }355}356357export class PolkadexHelper extends XcmChainHelper {358  assets: AssetsGroup<PolkadexHelper>;359  balance: SubstrateBalanceGroup<PolkadexHelper>;360  xTokens: XTokensGroup<PolkadexHelper>;361  xcm: XcmGroup<PolkadexHelper>;362  xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;363364  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {365    super(logger, options.helperBase ?? PolkadexHelper);366367    this.assets = new AssetsGroup(this);368    this.balance = new SubstrateBalanceGroup(this);369    this.xTokens = new XTokensGroup(this);370    this.xcm = new XcmGroup(this, 'polkadotXcm');371    this.xcmHelper = new PolkadexXcmHelperGroup(this);372  }373}374
after · js-packages/playgrounds/unique.xcm.ts
1import {ApiPromise, WsProvider} from '@polkadot/api';2import type {IKeyringPair} from '@polkadot/types/types';3import {ChainHelperBase, EthereumBalanceGroup, HelperGroup, SubstrateBalanceGroup, UniqueHelper} from './unique.js';4import type {ILogger, TSigner, TSubstrateAccount} from './types.js';5import type {AcalaAssetMetadata, DemocracyStandardAccountVote, MoonbeamAssetInfo} from './types.xcm.js';678export class XcmChainHelper extends ChainHelperBase {9  override async connect(wsEndpoint: string, _listeners?: any): Promise<void> {10    const wsProvider = new WsProvider(wsEndpoint);11    this.api = new ApiPromise({12      provider: wsProvider,13    });14    await this.api.isReadyOrError;15    this.network = await UniqueHelper.detectNetwork(this.api);16  }17}1819class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {20  async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {21    await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);22  }23}2425class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {26  makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {27    const apiPrefix = 'api.tx.assetManager.';2829    const registerTx = this.helper.constructApiCall(30      apiPrefix + 'registerForeignAsset',31      [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],32    );3334    const setUnitsTx = this.helper.constructApiCall(35      apiPrefix + 'setAssetUnitsPerSecond',36      [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],37    );3839    const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);40    const encodedProposal = batchCall?.method.toHex() || '';41    return encodedProposal;42  }4344  async assetTypeId(location: any) {45    return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);46  }47}4849class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {50  notePreimagePallet: string;5152  constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {53    super(helper);54    this.notePreimagePallet = options.notePreimagePallet;55  }5657  async notePreimage(signer: TSigner, encodedProposal: string) {58    await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);59  }6061  externalProposeMajority(proposal: any) {62    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);63  }6465  fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {66    return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);67  }6869  async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {70    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);71  }72}7374class DemocracyGroup<T extends ChainHelperBase> extends HelperGroup<T> {75  notePreimagePallet: string;7677  constructor(helper: T, options: { [key: string]: any } = {}) {78    super(helper);79    this.notePreimagePallet = options.notePreimagePallet;80  }8182  async notePreimage(signer: TSigner, encodedProposal: string) {83    await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);84  }8586  externalProposeMajority(proposal: any) {87    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);88  }8990  fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {91    return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);92  }9394  async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {95    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);96  }97}9899class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {100  collective: string;101102  constructor(helper: MoonbeamHelper, collective: string) {103    super(helper);104105    this.collective = collective;106  }107108  async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {109    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);110  }111112  async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {113    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);114  }115116  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {117    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);118  }119120  async proposalCount() {121    return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));122  }123}124125class CollectiveGroup<T extends ChainHelperBase> extends HelperGroup<T> {126  collective: string;127128  constructor(helper: T, palletName: string) {129    super(helper);130131    this.collective = palletName;132  }133134  async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {135    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);136  }137138  async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {139    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);140  }141142  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {143    await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);144  }145146  async proposalCount() {147    return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));148  }149}150class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {151  async whitelistToken(signer: TSigner, assetId: any) {152    await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);153  }154}155156export class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {157  async register(signer: TSigner, assetId: any, name: string, tokenPrefix: string, mode: 'NFT' | { Fungible: number }) {158    await this.helper.executeExtrinsic(159      signer,160      'api.tx.foreignAssets.forceRegisterForeignAsset',161      [{V3: assetId}, this.helper.util.str2vec(name), tokenPrefix, mode],162      true,163    );164  }165166  async foreignCollectionId(assetId: any) {167    return (await this.helper.callRpc('api.query.foreignAssets.foreignAssetToCollection', [assetId])).toJSON();168  }169}170171export class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {172  palletName: string;173174  constructor(helper: T, palletName: string) {175    super(helper);176177    this.palletName = palletName;178  }179180  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {181    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);182  }183184  async setSafeXcmVersion(signer: TSigner, version: number) {185    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);186  }187188  async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {189    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);190  }191192  async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {193    const destinationContent = {194      parents: 0,195      interior: {196        X1: {197          Parachain: destinationParaId,198        },199      },200    };201202    const beneficiaryContent = {203      parents: 0,204      interior: {205        X1: {206          AccountId32: {207            network: 'Any',208            id: targetAccount,209          },210        },211      },212    };213214    const assetsContent = [215      {216        id: {217          Concrete: {218            parents: 0,219            interior: 'Here',220          },221        },222        fun: {223          Fungible: amount,224        },225      },226    ];227228    let destination;229    let beneficiary;230    let assets;231232    if(xcmVersion == 2) {233      destination = {V1: destinationContent};234      beneficiary = {V1: beneficiaryContent};235      assets = {V1: assetsContent};236237    } else if(xcmVersion == 3) {238      destination = {V2: destinationContent};239      beneficiary = {V2: beneficiaryContent};240      assets = {V2: assetsContent};241242    } else {243      throw Error('Unknown XCM version: ' + xcmVersion);244    }245246    const feeAssetItem = 0;247248    await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);249  }250251  async send(signer: IKeyringPair, destination: any, message: any) {252    await this.helper.executeExtrinsic(253      signer,254      `api.tx.${this.palletName}.send`,255      [256        destination,257        message,258      ],259      true,260    );261  }262}263264export class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {265  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {266    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);267  }268269  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {270    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);271  }272273  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {274    await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);275  }276}277278279280export class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {281  async accounts(address: string, currencyId: any) {282    const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;283    return BigInt(free);284  }285}286287export class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {288  async create(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint) {289    await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);290  }291292  async forceCreate(signer: TSigner, assetId: number | bigint, admin: string, minimalBalance: bigint, isSufficient = true) {293    await this.helper.executeExtrinsic(signer, 'api.tx.assets.forceCreate', [assetId, admin, isSufficient, minimalBalance], true);294  }295296  async setMetadata(signer: TSigner, assetId: number | bigint, name: string, symbol: string, decimals: number) {297    await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);298  }299300  async mint(signer: TSigner, assetId: number | bigint, beneficiary: string, amount: bigint) {301    await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);302  }303304  async assetInfo(assetId: number | bigint) {305    return (await this.helper.callRpc('api.query.assets.asset', [assetId])).toJSON();306  }307308  async account(assetId: string | number | bigint, address: string) {309    const accountAsset = (310      await this.helper.callRpc('api.query.assets.account', [assetId, address])311    ).toJSON()! as any;312313    if(accountAsset !== null) {314      return BigInt(accountAsset['balance']);315    } else {316      return null;317    }318  }319}320321export class RelayHelper extends XcmChainHelper {322  balance: SubstrateBalanceGroup<RelayHelper>;323  xcm: XcmGroup<RelayHelper>;324325  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {326    super(logger, options.helperBase ?? RelayHelper);327328    this.balance = new SubstrateBalanceGroup(this);329    this.xcm = new XcmGroup(this, 'xcmPallet');330  }331}332333export class WestmintHelper extends XcmChainHelper {334  balance: SubstrateBalanceGroup<WestmintHelper>;335  xcm: XcmGroup<WestmintHelper>;336  assets: AssetsGroup<WestmintHelper>;337  xTokens: XTokensGroup<WestmintHelper>;338339  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {340    super(logger, options.helperBase ?? WestmintHelper);341342    this.balance = new SubstrateBalanceGroup(this);343    this.xcm = new XcmGroup(this, 'polkadotXcm');344    this.assets = new AssetsGroup(this);345    this.xTokens = new XTokensGroup(this);346  }347}348349export class MoonbeamHelper extends XcmChainHelper {350  balance: EthereumBalanceGroup<MoonbeamHelper>;351  assetManager: MoonbeamAssetManagerGroup;352  assets: AssetsGroup<MoonbeamHelper>;353  xTokens: XTokensGroup<MoonbeamHelper>;354  democracy: MoonbeamDemocracyGroup;355  collective: {356    council: MoonbeamCollectiveGroup,357    techCommittee: MoonbeamCollectiveGroup,358  };359360  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {361    super(logger, options.helperBase ?? MoonbeamHelper);362363    this.balance = new EthereumBalanceGroup(this);364    this.assetManager = new MoonbeamAssetManagerGroup(this);365    this.assets = new AssetsGroup(this);366    this.xTokens = new XTokensGroup(this);367    this.democracy = new MoonbeamDemocracyGroup(this, options);368    this.collective = {369      council: new MoonbeamCollectiveGroup(this, 'councilCollective'),370      techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),371    };372  }373}374375export class AstarHelper extends XcmChainHelper {376  balance: SubstrateBalanceGroup<AstarHelper>;377  assets: AssetsGroup<AstarHelper>;378  xcm: XcmGroup<AstarHelper>;379380  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {381    super(logger, options.helperBase ?? AstarHelper);382383    this.balance = new SubstrateBalanceGroup(this);384    this.assets = new AssetsGroup(this);385    this.xcm = new XcmGroup(this, 'polkadotXcm');386  }387}388389export class AcalaHelper extends XcmChainHelper {390  balance: SubstrateBalanceGroup<AcalaHelper>;391  assetRegistry: AcalaAssetRegistryGroup;392  xTokens: XTokensGroup<AcalaHelper>;393  tokens: TokensGroup<AcalaHelper>;394  xcm: XcmGroup<AcalaHelper>;395396  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {397    super(logger, options.helperBase ?? AcalaHelper);398399    this.balance = new SubstrateBalanceGroup(this);400    this.assetRegistry = new AcalaAssetRegistryGroup(this);401    this.xTokens = new XTokensGroup(this);402    this.tokens = new TokensGroup(this);403    this.xcm = new XcmGroup(this, 'polkadotXcm');404  }405}406407export class PolkadexHelper extends XcmChainHelper {408  assets: AssetsGroup<PolkadexHelper>;409  balance: SubstrateBalanceGroup<PolkadexHelper>;410  xTokens: XTokensGroup<PolkadexHelper>;411  xcm: XcmGroup<PolkadexHelper>;412  xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;413414  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {415    super(logger, options.helperBase ?? PolkadexHelper);416417    this.assets = new AssetsGroup(this);418    this.balance = new SubstrateBalanceGroup(this);419    this.xTokens = new XTokensGroup(this);420    this.xcm = new XcmGroup(this, 'polkadotXcm');421    this.xcmHelper = new PolkadexXcmHelperGroup(this);422  }423}424425export class HydraDxHelper extends XcmChainHelper {426  balance: SubstrateBalanceGroup<HydraDxHelper>;427  xcm: XcmGroup<HydraDxHelper>;428  xTokens: XTokensGroup<HydraDxHelper>;429  democracy: DemocracyGroup<HydraDxHelper>;430  collective: {431    council: CollectiveGroup<HydraDxHelper>,432    techCommittee: CollectiveGroup<HydraDxHelper>,433  };434435  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {436    super(logger, options.helperBase ?? HydraDxHelper);437    options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';438439    this.balance = new SubstrateBalanceGroup(this);440    this.xcm = new XcmGroup(this, 'polkadotXcm');441    this.xTokens = new XTokensGroup(this);442    this.democracy = new DemocracyGroup(this, options);443    this.collective = {444      council: new CollectiveGroup(this, 'council'),445      techCommittee: new CollectiveGroup(this, 'technicalCommittee'),446    };447  }448}449
modifiedjs-packages/tests/config.tsdiffbeforeafterboth
--- a/js-packages/tests/config.ts
+++ b/js-packages/tests/config.ts
@@ -29,6 +29,7 @@
   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',
+  hydraDxUrl: process.env.RELAY_HYDRADX_URL || 'ws://127.0.0.1:9951',
 };
 
 export default config;
modifiedjs-packages/tests/util/createHrmp.tsdiffbeforeafterboth
--- a/js-packages/tests/util/createHrmp.ts
+++ b/js-packages/tests/util/createHrmp.ts
@@ -27,6 +27,7 @@
       await bidirOpen(1001, 1004);
       await bidirOpen(1001, 1005);
       await bidirOpen(1001, 1006);
+      await bidirOpen(1001, 1007);
       break;
     default:
       throw new Error(`unknown hrmp config profile: ${profile}`);
modifiedjs-packages/tests/util/index.tsdiffbeforeafterboth
--- a/js-packages/tests/util/index.ts
+++ b/js-packages/tests/util/index.ts
@@ -11,7 +11,7 @@
 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 {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper, DevHydraDxHelper} from '@unique/playgrounds/unique.dev.js';
 import {dirname} from 'path';
 import {fileURLToPath} from 'url';
 
@@ -91,6 +91,8 @@
 
 export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);
 
+export const usingHydraDxPlaygrounds = (url: string, code: (helper: DevHydraDxHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevHydraDxHelper>(DevHydraDxHelper, url, code);
+
 export const MINIMUM_DONOR_FUND = 4_000_000n;
 export const DONOR_FUNDING = 4_000_000n;
 
modifiedjs-packages/tests/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth
--- a/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
+++ b/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
@@ -16,10 +16,10 @@
 
 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 {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds} 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';
+import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl,  hydraDxUrl,  moonbeamUrl, polkadexUrl, uniqueAssetId} from './xcm.types.js';
 
 const testHelper = new XcmTestHelper('unique');
 
@@ -364,3 +364,40 @@
     await testHelper.rejectReserveTransferUNQfrom('astar', alice);
   });
 });
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with HydraDx', () => {
+  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 helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+    });
+
+    await usingHydraDxPlaygrounds(hydraDxUrl, async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+    });
+  });
+
+  itSub('Should connect and send UNQ to HydraDx', async () => {
+    await testHelper.sendUnqTo('hydraDx', randomAccount);
+  });
+
+  itSub('Should connect to HydraDx and send UNQ back', async () => {
+    await testHelper.sendUnqBack('hydraDx', alice, randomAccount);
+  });
+
+  itSub('HydraDx can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('hydraDx', alice);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from HydraDx', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('hydraDx', alice);
+  });
+});
\ No newline at end of file
modifiedjs-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth
--- a/js-packages/tests/xcm/xcm.types.ts
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -1,6 +1,6 @@
 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 {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
 import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
 import config from '../config.js';
 
@@ -10,6 +10,7 @@
 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 HYDRADX_CHAIN = +(process.env.RELAY_HYDRADX_ID || 2034);
 
 export const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095);
 export const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000);
@@ -25,6 +26,7 @@
 export const moonbeamUrl = config.moonbeamUrl;
 export const astarUrl = config.astarUrl;
 export const polkadexUrl = config.polkadexUrl;
+export const hydraDxUrl = config.hydraDxUrl;
 
 export const karuraUrl = config.karuraUrl;
 export const moonriverUrl = config.moonriverUrl;
@@ -87,6 +89,7 @@
   moonriver: usingMoonriverPlaygrounds,
   karura: usingKaruraPlaygrounds,
   shiden: usingShidenPlaygrounds,
+  hydraDx: usingHydraDxPlaygrounds,
 } as const;
 type NetworkNames = keyof typeof NETWORKS;
 
@@ -108,6 +111,8 @@
       return KARURA_CHAIN;
     case 'shiden':
       return SHIDEN_CHAIN;
+    case 'hydraDx':
+      return HYDRADX_CHAIN;
   }
 }
 
@@ -127,6 +132,8 @@
       return karuraUrl;
     case 'shiden':
       return shidenUrl;
+    case 'hydraDx':
+      return hydraDxUrl;
   }
 }
 
@@ -277,7 +284,7 @@
         since the hash is being checked to ensure
         it matches what was sent.
       */
-        if(networkName == 'polkadex') {
+        if(networkName == 'polkadex' || networkName =='hydraDx') {
           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);