From 430b0201ea8376cff279bb267a8879159499e428 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 14 Dec 2023 09:47:33 +0000 Subject: [PATCH] Merge pull request #1046 from UniqueNetwork/ci/hydra-xcm-integration hydra xcm integration --- --- 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) --- 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'] }, --- 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'] }, --- a/.baedeker/xcm-unique.jsonnet +++ b/.baedeker/xcm-unique.jsonnet @@ -11,22 +11,25 @@ spec: {Genesis:{ chain: relay_spec, modify:: m.genericRelay($, hrmp = std.join([], [ - [[$.parachains[a].paraId, $.parachains[b].paraId, 8, 512], [$.parachains[b].paraId, $.parachains[a].paraId, 8, 512]], - for [a, b] in [ + // [[$.parachains[a].paraId, $.parachains[b].paraId, 8, 512], [$.parachains[b].paraId, $.parachains[a].paraId, 8, 512]], + // for [a, b] in [ // ['unique', 'acala'], // ['unique', 'moonbeam'], // ['unique', 'statemint'], // ['unique', 'astar'], // ['unique', 'polkadex'], - ] + // ] ])), }}, nodes: { [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] }, } --- /dev/null +++ b/.docker/Dockerfile-hydradx.j2 @@ -0,0 +1,46 @@ +# ===== Rust builder ===== +FROM ubuntu:22.04 as rust-builder +LABEL maintainer="Unique.Network" + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && \ + apt-get install -y curl cmake pkg-config libssl-dev git clang llvm libudev-dev protobuf-compiler && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN rustup toolchain install {{ RUST_TOOLCHAIN }} && \ + rustup target add wasm32-unknown-unknown --toolchain {{ RUST_TOOLCHAIN }} && \ + rustup default {{ RUST_TOOLCHAIN }} && \ + rustup target list --installed && \ + rustup show + +RUN mkdir /unique_parachain +WORKDIR /unique_parachain + +# ===== BUILD ACALA ===== +FROM rust-builder as builder-hydradx-bin + +WORKDIR /unique_parachain + +RUN git clone --depth 1 -b {{ HYDRADX_BUILD_BRANCH }} https://github.com/galacticcouncil/HydraDX-node.git + +RUN --mount=type=cache,target=/cargo-home/registry \ + --mount=type=cache,target=/cargo-home/git \ + --mount=type=cache,target=/unique_parachain/parachain/target \ + cd HydraDX-node && \ + CARGO_INCREMENTAL=0 cargo build --release --locked && \ + mkdir /unique_parachain/Hydradx && \ + mv ./target/release/hydradx /unique_parachain/Hydradx/hydradx + +# ===== BIN ====== + +FROM ubuntu:22.04 as builder-hydradx + +COPY --from=builder-hydradx-bin /unique_parachain/Hydradx/hydradx /bin/hydradx +ENTRYPOINT ["/bin/hydradx"] --- 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 --- a/.github/workflows/xcm.yml +++ b/.github/workflows/xcm.yml @@ -37,9 +37,9 @@ id: create_matrix with: matrix: | - network {opal}, relay_branch {${{ env.UNIQUEWEST_MAINNET_BRANCH }}}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.WESTMINT_BUILD_BRANCH }}}, astar_version {${{ env.ASTAR_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testXcmOpal}, runtime_features {opal-runtime} - network {quartz}, relay_branch {${{ env.KUSAMA_MAINNET_BRANCH }}}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}}, astar_version {${{ env.SHIDEN_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testFullXcmQuartz}, runtime_features {quartz-runtime} - network {unique}, relay_branch {${{ env.POLKADOT_MAINNET_BRANCH }}}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINT_BUILD_BRANCH }}}, astar_version {${{ env.ASTAR_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testFullXcmUnique}, runtime_features {unique-runtime} + network {opal}, relay_branch {${{ env.UNIQUEWEST_MAINNET_BRANCH }}}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.WESTMINT_BUILD_BRANCH }}}, astar_version {${{ env.ASTAR_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, hydradx_version {${{ env.HYDRADX_BUILD_BRANCH }}}, runtest {testXcmOpal}, runtime_features {opal-runtime} + network {quartz}, relay_branch {${{ env.KUSAMA_MAINNET_BRANCH }}}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}}, astar_version {${{ env.SHIDEN_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, hydradx_version {${{ env.HYDRADX_BUILD_BRANCH }}}, runtest {testFullXcmQuartz}, runtime_features {quartz-runtime} + network {unique}, relay_branch {${{ env.POLKADOT_MAINNET_BRANCH }}}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINT_BUILD_BRANCH }}}, astar_version {${{ env.ASTAR_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, hydradx_version {${{ env.HYDRADX_BUILD_BRANCH }}}, runtest {testFullXcmUnique}, runtime_features {unique-runtime} xcm: needs: prepare-execution-marix @@ -280,6 +280,40 @@ fi shell: bash + # Check Hydradx version and build it if it doesn't exist in repository + - name: Generate ENV related extend Dockerfile file for Hydradx + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/Dockerfile-hydradx.j2 + output_file: .docker/Dockerfile-hydradx.${{ matrix.hydradx_version }}.yml + variables: | + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + HYDRADX_BUILD_BRANCH=${{ matrix.hydradx_version }} + + - name: Check if the dockerhub repository contains the needed HYDRADX version + run: | + # aquire token + TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${{ secrets.CORE_DOCKERHUB_USERNAME }}'", "password": "'${{ secrets.CORE_DOCKERHUB_TOKEN }}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) + export TOKEN=$TOKEN + + # Get TAGS from DOCKERHUB repository + HYDRADX_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/uniquenetwork/builder-hydradx/tags/?page_size=100 | jq -r '."results"[]["name"]') + # Show TAGS + echo "HYDRADX TAGS:" + echo $HYDRADX_TAGS + # Check correct version HYDRADX and build it if it doesn't exist in HYDRADX TAGS + if [[ ${HYDRADX_TAGS[*]} =~ (^|[[:space:]])"${{ matrix.hydradx_version }}"($|[[:space:]]) ]]; then + echo "Repository has needed HYDRADX version"; + docker pull uniquenetwork/builder-hydradx:${{ matrix.hydradx_version }} + else + echo "Repository has not needed HYDRADX version, so build it"; + cd .docker/ && docker build --file ./Dockerfile-hydradx.${{ matrix.hydradx_version }}.yml --tag uniquenetwork/builder-hydradx:${{ matrix.hydradx_version }} . + echo "Push needed HYDRADX version to the repository"; + docker push uniquenetwork/builder-hydradx:${{ matrix.hydradx_version }} + fi + shell: bash + + - name: Build unique-chain run: | docker build --file .docker/Dockerfile-unique \ @@ -318,6 +352,7 @@ snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/cumulus':{dockerImage:'uniquenetwork/builder-cumulus:${{ matrix.cumulus_version }}'}}) snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/astar':{dockerImage:'uniquenetwork/builder-astar:${{ matrix.astar_version }}'}}) snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/polkadex':{dockerImage:'uniquenetwork/builder-polkadex:${{ matrix.polkadex_version }}'}}) + snippet:(import 'baedeker-library/ops/rewrites.libsonnet').rewriteNodePaths({'bin/hydradx':{dockerImage:'uniquenetwork/builder-hydradx:${{ matrix.hydradx_version }}'}}) - name: Upload network config uses: actions/upload-artifact@v3 --- 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" } --- 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]": { --- 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,91 @@ } } +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, + ); + + 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; --- a/js-packages/playgrounds/unique.xcm.ts +++ b/js-packages/playgrounds/unique.xcm.ts @@ -71,6 +71,31 @@ } } +class DemocracyGroup extends HelperGroup { + notePreimagePallet: string; + + constructor(helper: T, options: { [key: string]: any } = {}) { + super(helper); + this.notePreimagePallet = options.notePreimagePallet; + } + + async notePreimage(signer: TSigner, encodedProposal: string) { + await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true); + } + + externalProposeMajority(proposal: any) { + return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]); + } + + fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) { + return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]); + } + + async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) { + await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true); + } +} + class MoonbeamCollectiveGroup extends HelperGroup { collective: string; @@ -97,6 +122,31 @@ } } +class CollectiveGroup extends HelperGroup { + collective: string; + + constructor(helper: T, palletName: string) { + super(helper); + + this.collective = palletName; + } + + async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) { + await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true); + } + + async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) { + await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true); + } + + async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) { + await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true); + } + + async proposalCount() { + return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])); + } +} class PolkadexXcmHelperGroup extends HelperGroup { async whitelistToken(signer: TSigner, assetId: any) { await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true); @@ -372,3 +422,28 @@ } } +export class HydraDxHelper extends XcmChainHelper { + balance: SubstrateBalanceGroup; + xcm: XcmGroup; + xTokens: XTokensGroup; + democracy: DemocracyGroup; + collective: { + council: CollectiveGroup, + techCommittee: CollectiveGroup, + }; + + constructor(logger?: ILogger, options: { [key: string]: any } = {}) { + super(logger, options.helperBase ?? HydraDxHelper); + options.notePreimagePallet = options.notePreimagePallet ?? 'preimage'; + + this.balance = new SubstrateBalanceGroup(this); + this.xcm = new XcmGroup(this, 'polkadotXcm'); + this.xTokens = new XTokensGroup(this); + this.democracy = new DemocracyGroup(this, options); + this.collective = { + council: new CollectiveGroup(this, 'council'), + techCommittee: new CollectiveGroup(this, 'technicalCommittee'), + }; + } +} + --- 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; --- 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}`); --- 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) => Promise) => usingPlaygroundsGeneral(DevPolkadexHelper, url, code); +export const usingHydraDxPlaygrounds = (url: string, code: (helper: DevHydraDxHelper, privateKey: (seed: string) => Promise) => Promise) => usingPlaygroundsGeneral(DevHydraDxHelper, url, code); + export const MINIMUM_DONOR_FUND = 4_000_000n; export const DONOR_FUNDING = 4_000_000n; --- 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 --- 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); -- gitstuff