difftreelog
feat(ci) xcm tests for hydraDx
in: master
14 files changed
.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)
.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']
},
.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']
},
.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]
},
}
.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
.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"
}
js-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]": {
js-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;
js-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth1import {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}449js-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;
js-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}`);
js-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;
js-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
js-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);