difftreelog
Merge pull request #1046 from UniqueNetwork/ci/hydra-xcm-integration
in: master
hydra xcm integration
16 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
@@ -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]
},
}
.docker/Dockerfile-hydradx.j2diffbeforeafterboth--- /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"]
.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
.github/workflows/xcm.ymldiffbeforeafterboth--- 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
.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,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;
js-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth--- a/js-packages/playgrounds/unique.xcm.ts
+++ b/js-packages/playgrounds/unique.xcm.ts
@@ -71,6 +71,31 @@
}
}
+class DemocracyGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ 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<MoonbeamHelper> {
collective: string;
@@ -97,6 +122,31 @@
}
}
+class CollectiveGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+ 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<T extends ChainHelperBase> extends HelperGroup<T> {
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<HydraDxHelper>;
+ xcm: XcmGroup<HydraDxHelper>;
+ xTokens: XTokensGroup<HydraDxHelper>;
+ democracy: DemocracyGroup<HydraDxHelper>;
+ collective: {
+ council: CollectiveGroup<HydraDxHelper>,
+ techCommittee: CollectiveGroup<HydraDxHelper>,
+ };
+
+ 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'),
+ };
+ }
+}
+
js-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.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import * as path from 'path';5import * as crypto from 'crypto';6import type {IKeyringPair} from '@polkadot/types/types/interfaces';7import chai from 'chai';8import chaiAsPromised from 'chai-as-promised';9import chaiSubset from 'chai-subset';10import {Context} from 'mocha';11import config from '../config.js';12import {ChainHelperBase} from '@unique/playgrounds/unique.js';13import type {ILogger} from '@unique/playgrounds/types.js';14import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper} from '@unique/playgrounds/unique.dev.js';15import {dirname} from 'path';16import {fileURLToPath} from 'url';1718chai.config.truncateThreshold = 0;19chai.use(chaiAsPromised);20chai.use(chaiSubset);21export const expect = chai.expect;2223const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex');2425export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`;2627async function usingPlaygroundsGeneral<T extends ChainHelperBase, R = void>(28 helperType: new (logger: ILogger) => T,29 url: string,30 code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise<IKeyringPair>) => Promise<R>,31): Promise<R> {32 const silentConsole = new SilentConsole();33 silentConsole.enable();3435 const helper = new helperType(new SilentLogger());36 let result;37 try {38 await helper.connect(url);39 const ss58Format = helper.chain.getChainProperties().ss58Format;40 const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => {41 if(typeof seed === 'string') {42 return helper.util.fromSeed(seed, ss58Format);43 }44 if(seed.url) {45 const {filename} = makeNames(seed.url);46 seed.filename = filename;47 } else if(seed.filename) {48 // Pass49 } else {50 throw new Error('no url nor filename set');51 }52 const actualSeed = getTestSeed(seed.filename);53 let account = helper.util.fromSeed(actualSeed, ss58Format);54 // here's to hoping that no55 if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) {56 console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);57 account = helper.util.fromSeed('//Alice', ss58Format);58 }59 return account;60 };61 result = await code(helper, privateKey);62 }63 finally {64 await helper.disconnect();65 silentConsole.disable();66 }67 return result as any as R;68}6970export const usingPlaygrounds = <R = void>(code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<R>, url: string = config.substrateUrl) => usingPlaygroundsGeneral<DevUniqueHelper, R>(DevUniqueHelper, url, code);7172export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);7374export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);7576export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);7778export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);7980export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);8182export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);8384export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);8586export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);8788export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);8990export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);9192export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);9394export const MINIMUM_DONOR_FUND = 4_000_000n;95export const DONOR_FUNDING = 4_000_000n;9697// App-promotion periods:98export const LOCKING_PERIOD = 12n; // 12 blocks of relay99export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain100101// Native contracts102export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';103export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049';104105export enum Pallets {106 Inflation = 'inflation',107 ReFungible = 'refungible',108 Fungible = 'fungible',109 NFT = 'nonfungible',110 Scheduler = 'scheduler',111 UniqueScheduler = 'uniqueScheduler',112 AppPromotion = 'apppromotion',113 CollatorSelection = 'collatorselection',114 Session = 'session',115 Identity = 'identity',116 Democracy = 'democracy',117 Council = 'council',118 //CouncilMembership = 'councilmembership',119 TechnicalCommittee = 'technicalcommittee',120 Fellowship = 'fellowshipcollective',121 Preimage = 'preimage',122 Maintenance = 'maintenance',123 TestUtils = 'testutils',124}125126export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) {127 const missingPallets = helper.fetchMissingPalletNames(requiredPallets);128129 if(missingPallets.length > 0) {130 const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;131 console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg);132 test.skip();133 }134}135136export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {137 (opts.only ? it.only :138 opts.skip ? it.skip : it)(name, async function () {139 await usingPlaygrounds(async (helper, privateKey) => {140 if(opts.requiredPallets) {141 requirePalletsOrSkip(this, helper, opts.requiredPallets);142 }143144 await cb({helper, privateKey});145 });146 });147}148export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {149 return itSub(name, cb, {requiredPallets: required, ...opts});150}151itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {only: true});152itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {skip: true});153154itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {only: true});155itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});156itSub.ifWithPallets = itSubIfWithPallet;157158export type SchedKind = 'anon' | 'named';159160export function itSched(161 name: string,162 cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any,163 opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},164) {165 itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);166 itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts);167}168itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {only: true});169itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {skip: true});170itSched.ifWithPallets = itSchedIfWithPallets;171172function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {173 return itSched(name, cb, {requiredPallets: required, ...opts});174}175176export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {177 (process.env.RUN_XCM_TESTS && !opts.skip178 ? describe179 : describe.skip)(title, fn);180}181182describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true});183184export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {185 (process.env.RUN_GOV_TESTS && !opts.skip186 ? describe187 : describe.skip)(title, fn);188}189190describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true});191192export function sizeOfInt(i: number) {193 if(i < 0 || i > 0xffffffff) throw new Error('out of range');194 if(i < 0b11_1111) {195 return 1;196 } else if(i < 0b11_1111_1111_1111) {197 return 2;198 } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) {199 return 4;200 } else {201 return 5;202 }203}204205const UTF8_ENCODER = new TextEncoder();206export function sizeOfEncodedStr(v: string) {207 const encoded = UTF8_ENCODER.encode(v);208 return sizeOfInt(encoded.length) + encoded.length;209}210211export function sizeOfProperty(prop: {key: string, value: string}) {212 return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value);213}214215export function makeNames(url: string) {216 const filename = fileURLToPath(url);217 return {218 filename,219 dirname: dirname(filename),220 };221}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import * as path from 'path';5import * as crypto from 'crypto';6import type {IKeyringPair} from '@polkadot/types/types/interfaces';7import chai from 'chai';8import chaiAsPromised from 'chai-as-promised';9import chaiSubset from 'chai-subset';10import {Context} from 'mocha';11import config from '../config.js';12import {ChainHelperBase} from '@unique/playgrounds/unique.js';13import type {ILogger} from '@unique/playgrounds/types.js';14import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper, DevHydraDxHelper} from '@unique/playgrounds/unique.dev.js';15import {dirname} from 'path';16import {fileURLToPath} from 'url';1718chai.config.truncateThreshold = 0;19chai.use(chaiAsPromised);20chai.use(chaiSubset);21export const expect = chai.expect;2223const getTestHash = (filename: string) => crypto.createHash('md5').update(filename).digest('hex');2425export const getTestSeed = (filename: string) => `//Alice+${getTestHash(filename)}`;2627async function usingPlaygroundsGeneral<T extends ChainHelperBase, R = void>(28 helperType: new (logger: ILogger) => T,29 url: string,30 code: (helper: T, privateKey: (seed: string | { filename?: string, url?: string, ignoreFundsPresence?: boolean }) => Promise<IKeyringPair>) => Promise<R>,31): Promise<R> {32 const silentConsole = new SilentConsole();33 silentConsole.enable();3435 const helper = new helperType(new SilentLogger());36 let result;37 try {38 await helper.connect(url);39 const ss58Format = helper.chain.getChainProperties().ss58Format;40 const privateKey = async (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => {41 if(typeof seed === 'string') {42 return helper.util.fromSeed(seed, ss58Format);43 }44 if(seed.url) {45 const {filename} = makeNames(seed.url);46 seed.filename = filename;47 } else if(seed.filename) {48 // Pass49 } else {50 throw new Error('no url nor filename set');51 }52 const actualSeed = getTestSeed(seed.filename);53 let account = helper.util.fromSeed(actualSeed, ss58Format);54 // here's to hoping that no55 if(!seed.ignoreFundsPresence && ((helper as any)['balance'] == undefined || await (helper as any).balance.getSubstrate(account.address) < MINIMUM_DONOR_FUND)) {56 console.warn(`${path.basename(seed.filename)}: Not enough funds present on the filename account. Using the default one as the donor instead.`);57 account = helper.util.fromSeed('//Alice', ss58Format);58 }59 return account;60 };61 result = await code(helper, privateKey);62 }63 finally {64 await helper.disconnect();65 silentConsole.disable();66 }67 return result as any as R;68}6970export const usingPlaygrounds = <R = void>(code: (helper: DevUniqueHelper, privateKey: (seed: string | {filename?: string, url?: string, ignoreFundsPresence?: boolean}) => Promise<IKeyringPair>) => Promise<R>, url: string = config.substrateUrl) => usingPlaygroundsGeneral<DevUniqueHelper, R>(DevUniqueHelper, url, code);7172export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);7374export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);7576export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);7778export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);7980export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);8182export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);8384export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);8586export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);8788export const usingAstarPlaygrounds = (url: string, code: (helper: DevAstarHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevAstarHelper>(DevAstarHelper, url, code);8990export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);9192export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);9394export const usingHydraDxPlaygrounds = (url: string, code: (helper: DevHydraDxHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevHydraDxHelper>(DevHydraDxHelper, url, code);9596export const MINIMUM_DONOR_FUND = 4_000_000n;97export const DONOR_FUNDING = 4_000_000n;9899// App-promotion periods:100export const LOCKING_PERIOD = 12n; // 12 blocks of relay101export const UNLOCKING_PERIOD = 6n; // 6 blocks of parachain102103// Native contracts104export const COLLECTION_HELPER = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';105export const CONTRACT_HELPER = '0x842899ECF380553E8a4de75bF534cdf6fBF64049';106107export enum Pallets {108 Inflation = 'inflation',109 ReFungible = 'refungible',110 Fungible = 'fungible',111 NFT = 'nonfungible',112 Scheduler = 'scheduler',113 UniqueScheduler = 'uniqueScheduler',114 AppPromotion = 'apppromotion',115 CollatorSelection = 'collatorselection',116 Session = 'session',117 Identity = 'identity',118 Democracy = 'democracy',119 Council = 'council',120 //CouncilMembership = 'councilmembership',121 TechnicalCommittee = 'technicalcommittee',122 Fellowship = 'fellowshipcollective',123 Preimage = 'preimage',124 Maintenance = 'maintenance',125 TestUtils = 'testutils',126}127128export function requirePalletsOrSkip(test: Context, helper: DevUniqueHelper, requiredPallets: readonly string[]) {129 const missingPallets = helper.fetchMissingPalletNames(requiredPallets);130131 if(missingPallets.length > 0) {132 const skipMsg = `\tSkipping test '${test.test?.title}'.\n\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;133 console.warn('\x1b[38:5:208m%s\x1b[0m', skipMsg);134 test.skip();135 }136}137138export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {139 (opts.only ? it.only :140 opts.skip ? it.skip : it)(name, async function () {141 await usingPlaygrounds(async (helper, privateKey) => {142 if(opts.requiredPallets) {143 requirePalletsOrSkip(this, helper, opts.requiredPallets);144 }145146 await cb({helper, privateKey});147 });148 });149}150export function itSubIfWithPallet(name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: readonly string[] } = {}) {151 return itSub(name, cb, {requiredPallets: required, ...opts});152}153itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {only: true});154itSub.skip = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {skip: true});155156itSubIfWithPallet.only = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {only: true});157itSubIfWithPallet.skip = (name: string, required: readonly string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});158itSub.ifWithPallets = itSubIfWithPallet;159160export type SchedKind = 'anon' | 'named';161162export function itSched(163 name: string,164 cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any,165 opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},166) {167 itSub(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);168 itSub(name + ' (named scheduling)', (apis) => cb('named', apis), opts);169}170itSched.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {only: true});171itSched.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSched(name, cb, {skip: true});172itSched.ifWithPallets = itSchedIfWithPallets;173174function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {175 return itSched(name, cb, {requiredPallets: required, ...opts});176}177178export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {179 (process.env.RUN_XCM_TESTS && !opts.skip180 ? describe181 : describe.skip)(title, fn);182}183184describeXCM.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeXCM(name, fn, {skip: true});185186export function describeGov(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {187 (process.env.RUN_GOV_TESTS && !opts.skip188 ? describe189 : describe.skip)(title, fn);190}191192describeGov.skip = (name: string, fn: (this: Mocha.Suite) => void) => describeGov(name, fn, {skip: true});193194export function sizeOfInt(i: number) {195 if(i < 0 || i > 0xffffffff) throw new Error('out of range');196 if(i < 0b11_1111) {197 return 1;198 } else if(i < 0b11_1111_1111_1111) {199 return 2;200 } else if(i < 0b11_1111_1111_1111_1111_1111_1111_1111) {201 return 4;202 } else {203 return 5;204 }205}206207const UTF8_ENCODER = new TextEncoder();208export function sizeOfEncodedStr(v: string) {209 const encoded = UTF8_ENCODER.encode(v);210 return sizeOfInt(encoded.length) + encoded.length;211}212213export function sizeOfProperty(prop: {key: string, value: string}) {214 return sizeOfEncodedStr(prop.key) + sizeOfEncodedStr(prop.value);215}216217export function makeNames(url: string) {218 const filename = fileURLToPath(url);219 return {220 filename,221 dirname: dirname(filename),222 };223}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);