diff --git a/js-packages/playgrounds/unique.ts b/js-packages/playgrounds/unique.ts
index e000ead897..dc3a5aa633 100644
--- a/js-packages/playgrounds/unique.ts
+++ b/js-packages/playgrounds/unique.ts
@@ -38,6 +38,7 @@ import type {
TSubstrateAccount,
TNetworks,
IEthCrossAccountId,
+ IPhasicEvent,
} from './types.js';
import type {RuntimeDispatchInfo} from '@polkadot/types/interfaces';
@@ -368,6 +369,32 @@ class UniqueEventHelper {
return parsedEvents;
}
+
+ public static extractPhasicEvents(events: { event: any, phase: any }[]): IPhasicEvent[] {
+ const parsedEvents: IPhasicEvent[] = [];
+
+ events.forEach((record) => {
+ const {event, phase} = record;
+
+ const eventData: IEvent = {
+ section: event.section.toString(),
+ method: event.method.toString(),
+ index: this.extractIndex(event.index),
+
+ // assigned without any transformation for compatibility with the `ITransactionResult` events
+ data: event.data,
+
+ phase: phase.toJSON(),
+ };
+
+ parsedEvents.push({
+ phase,
+ event: eventData,
+ });
+ });
+
+ return parsedEvents;
+ }
}
const InvalidTypeSymbol = Symbol('Invalid type');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
diff --git a/js-packages/scripts/createHrmp.ts b/js-packages/scripts/createHrmp.ts
index ec5ab725d5..0b56dfec33 100644
--- a/js-packages/scripts/createHrmp.ts
+++ b/js-packages/scripts/createHrmp.ts
@@ -29,6 +29,8 @@ await usingPlaygrounds(async (helper, privateKey) => {
default:
throw new Error(`unknown hrmp config profile: ${profile}`);
}
+
+ await helper.wait.newSessions(1);
}, config.relayUrl);
// We miss disconnect/unref somewhere.
process.exit(0);
diff --git a/js-packages/test-utils/index.ts b/js-packages/test-utils/index.ts
index 85f39e7193..fa913637eb 100644
--- a/js-packages/test-utils/index.ts
+++ b/js-packages/test-utils/index.ts
@@ -13,7 +13,7 @@ import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
import * as defs from '@unique-nft/opal-testnet-types/definitions.js';
import type {IKeyringPair} from '@polkadot/types/types';
import type {EventRecord} from '@polkadot/types/interfaces';
-import type {ICrossAccountId, ILogger, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from '@unique-nft/playgrounds/types.js';
+import type {ICrossAccountId, ILogger, IPhasicEvent, IPovInfo, ISchedulerOptions, ITransactionResult, TSigner} from '@unique-nft/playgrounds/types.js';
import type {FrameSystemEventRecord, XcmV2TraitsError, StagingXcmV4TraitsOutcome} from '@polkadot/types/lookup';
import type {SignerOptions, VoidFn} from '@polkadot/api/types';
import {spawnSync} from 'child_process';
@@ -102,13 +102,13 @@ function EventHelper(section: string, method: string, wrapEvent: (data: any[]) =
.map(e => this.wrapEvent(e.event.data));
}
- find(txres: ITransactionResult) {
- const e = txres.result.events.find(e => e.event.section === section && e.event.method === method);
+ find(events: IPhasicEvent[]) {
+ const e = events.find(e => e.event.section === section && e.event.method === method);
return e ? this.wrapEvent(e.event.data) : null;
}
- expect(txres: ITransactionResult) {
- const e = this.find(txres);
+ expect(events: IPhasicEvent[]) {
+ const e = this.find(events);
if(e) {
return e;
} else {
@@ -274,6 +274,18 @@ export class Event {
}));
};
+ static XcmPallet = class extends EventSection('xcmPallet') {
+ static Sent = this.Method('Sent', data => ({
+ messageHash: eventJsonData(data, 3),
+ }));
+ };
+
+ static PolkadotXcm = class extends EventSection('polkadotXcm') {
+ static Sent = this.Method('Sent', data => ({
+ messageHash: eventJsonData(data, 3),
+ }));
+ };
+
static MessageQueue = class extends EventSection('messageQueue') {
static Processed = this.Method('Processed', data => ({
messageHash: eventJsonData(data, 0),
diff --git a/js-packages/tests/sub/governance/council.test.ts b/js-packages/tests/sub/governance/council.test.ts
index d963370276..e3557d0cee 100644
--- a/js-packages/tests/sub/governance/council.test.ts
+++ b/js-packages/tests/sub/governance/council.test.ts
@@ -39,7 +39,7 @@ describeGov('Governance: Council tests', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
@@ -61,7 +61,7 @@ describeGov('Governance: Council tests', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
@@ -92,7 +92,7 @@ describeGov('Governance: Council tests', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
@@ -145,7 +145,7 @@ describeGov('Governance: Council tests', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
@@ -153,7 +153,7 @@ describeGov('Governance: Council tests', () => {
await helper.wait.newBlocks(councilMotionDuration);
const closeResult = await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
- const closeEvent = Event.Council.Closed.expect(closeResult);
+ const closeEvent = Event.Council.Closed.expect(closeResult.result.events);
const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as string[];
expect(closeEvent.yes).to.be.equal(members.length);
});
@@ -396,7 +396,7 @@ describeGov('Governance: Council tests', () => {
itSub('[Negative] Council cannot cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex)))
.to.be.rejectedWith(/BadOrigin/);
@@ -405,7 +405,7 @@ describeGov('Governance: Council tests', () => {
itSub('[Negative] Council member cannot cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
await expect(helper.council.collective.execute(
counselors.alex,
@@ -445,7 +445,7 @@ describeGov('Governance: Council tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))
.to.be.rejectedWith(/BadOrigin/);
@@ -462,7 +462,7 @@ describeGov('Governance: Council tests', () => {
proposal,
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await expect(helper.council.collective.execute(
counselors.alex,
helper.fellowship.referenda.cancelCall(referendumIndex),
@@ -478,7 +478,7 @@ describeGov('Governance: Council tests', () => {
councilSize,
);
- const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
diff --git a/js-packages/tests/sub/governance/democracy.test.ts b/js-packages/tests/sub/governance/democracy.test.ts
index 37f91a6be9..fa79b3b404 100644
--- a/js-packages/tests/sub/governance/democracy.test.ts
+++ b/js-packages/tests/sub/governance/democracy.test.ts
@@ -35,7 +35,7 @@ describeGov('Governance: Democracy tests', () => {
{After: 0},
);
- const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await voteUnanimouslyInFellowship(helper, fellows, democracyTrackMinRank, fellowshipReferendumIndex);
await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex);
diff --git a/js-packages/tests/sub/governance/electsudo.test.ts b/js-packages/tests/sub/governance/electsudo.test.ts
index dd5566c941..9983f7a82e 100644
--- a/js-packages/tests/sub/governance/electsudo.test.ts
+++ b/js-packages/tests/sub/governance/electsudo.test.ts
@@ -62,7 +62,7 @@ describeGov('Governance: Elect Sudo', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.Council.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
diff --git a/js-packages/tests/sub/governance/fellowship.test.ts b/js-packages/tests/sub/governance/fellowship.test.ts
index f44126b528..07c00e643e 100644
--- a/js-packages/tests/sub/governance/fellowship.test.ts
+++ b/js-packages/tests/sub/governance/fellowship.test.ts
@@ -30,7 +30,7 @@ describeGov('Governance: Fellowship tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, referendumIndex);
await helper.fellowship.referenda.placeDecisionDeposit(donor, referendumIndex);
@@ -85,7 +85,7 @@ describeGov('Governance: Fellowship tests', () => {
defaultEnactmentMoment,
);
- const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, fellowshipReferendumIndex);
await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex);
@@ -116,7 +116,7 @@ describeGov('Governance: Fellowship tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex);
expect(referendumInfo.ongoing.track, `${memberIdx}-th member of rank #${rank}: proposal #${referendumIndex} is on invalid track`)
.to.be.equal(democracyTrackId);
@@ -134,7 +134,7 @@ describeGov('Governance: Fellowship tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
let expectedAyes = 0;
for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) {
@@ -171,7 +171,7 @@ describeGov('Governance: Fellowship tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) {
const rankMembers = members[rank];
@@ -253,7 +253,7 @@ describeGov('Governance: Fellowship tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
for(let rank = 0; rank < democracyTrackMinRank; rank++) {
for(const member of members[rank]) {
@@ -304,7 +304,7 @@ describeGov('Governance: Fellowship tests', () => {
itSub('[Negative] FellowshipProposition cannot cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
await testBadFellowshipProposal(helper, helper.democracy.cancelProposalCall(proposalIndex));
});
diff --git a/js-packages/tests/sub/governance/financialCouncil.test.ts b/js-packages/tests/sub/governance/financialCouncil.test.ts
index 549f958535..a2afecbb0d 100644
--- a/js-packages/tests/sub/governance/financialCouncil.test.ts
+++ b/js-packages/tests/sub/governance/financialCouncil.test.ts
@@ -36,7 +36,7 @@ describeGov('Governance: Financial Council tests', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
@@ -56,7 +56,7 @@ describeGov('Governance: Financial Council tests', () => {
moreThanHalfCouncilThreshold,
);
- const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult);
+ const councilProposedEvent = Event.FinCouncil.Proposed.expect(proposeResult.result.events);
const proposalIndex = councilProposedEvent.proposalIndex;
const proposalHash = councilProposedEvent.proposalHash;
@@ -118,7 +118,7 @@ describeGov('Governance: Financial Council tests', () => {
itSub('[Negative] FinCouncil can\'t cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
await expect(proposalFromAllFinCouncil(helper.democracy.cancelProposalCall(proposalIndex)))
.rejectedWith('BadOrigin');
@@ -126,7 +126,7 @@ describeGov('Governance: Financial Council tests', () => {
itSub('[Negative] FinCouncil member cannot cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
await expect(helper.finCouncil.collective.execute(
finCounselors.andy,
@@ -201,7 +201,7 @@ describeGov('Governance: Financial Council tests', () => {
proposal,
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await expect(proposalFromAllFinCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))
.rejectedWith('BadOrigin');
});
@@ -218,7 +218,7 @@ describeGov('Governance: Financial Council tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await expect(helper.finCouncil.collective.execute(
finCounselors.andy,
diff --git a/js-packages/tests/sub/governance/technicalCommittee.test.ts b/js-packages/tests/sub/governance/technicalCommittee.test.ts
index 2dea4ef70e..02d1952438 100644
--- a/js-packages/tests/sub/governance/technicalCommittee.test.ts
+++ b/js-packages/tests/sub/governance/technicalCommittee.test.ts
@@ -46,7 +46,7 @@ describeGov('Governance: Technical Committee tests', () => {
allTechCommitteeThreshold,
);
- const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);
+ const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult.result.events);
const proposalIndex = commiteeProposedEvent.proposalIndex;
const proposalHash = commiteeProposedEvent.proposalHash;
@@ -56,9 +56,9 @@ describeGov('Governance: Technical Committee tests', () => {
await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true);
const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex);
- Event.TechnicalCommittee.Closed.expect(closeResult);
- Event.TechnicalCommittee.Approved.expect(closeResult);
- const {result} = Event.TechnicalCommittee.Executed.expect(closeResult);
+ Event.TechnicalCommittee.Closed.expect(closeResult.result.events);
+ Event.TechnicalCommittee.Approved.expect(closeResult.result.events);
+ const {result} = Event.TechnicalCommittee.Executed.expect(closeResult.result.events);
expect(result).to.eq('Ok');
return closeResult;
@@ -72,15 +72,15 @@ describeGov('Governance: Technical Committee tests', () => {
await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));
- Event.Democracy.Started.expect(fastTrackProposal);
+ Event.Democracy.Started.expect(fastTrackProposal.result.events);
});
itSub('TechComm can cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex));
- Event.Democracy.ProposalCanceled.expect(cancelProposal);
+ Event.Democracy.ProposalCanceled.expect(cancelProposal.result.events);
});
itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => {
@@ -89,7 +89,7 @@ describeGov('Governance: Technical Committee tests', () => {
const referendumIndex = startedEvent.referendumIndex;
const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex));
- Event.Democracy.Cancelled.expect(emergencyCancelProposal);
+ Event.Democracy.Cancelled.expect(emergencyCancelProposal.result.events);
});
itSub('TechComm member can veto Democracy proposals', async ({helper}) => {
@@ -100,7 +100,7 @@ describeGov('Governance: Technical Committee tests', () => {
techcomms.andy,
helper.democracy.vetoExternalCall(preimageHash),
);
- Event.Democracy.Vetoed.expect(vetoExternalCall);
+ Event.Democracy.Vetoed.expect(vetoExternalCall.result.events);
});
itSub('TechComm can cancel Fellowship referendums', async ({helper}) => {
@@ -114,9 +114,9 @@ describeGov('Governance: Technical Committee tests', () => {
proposal,
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex));
- Event.FellowshipReferenda.Cancelled.expect(cancelProposal);
+ Event.FellowshipReferenda.Cancelled.expect(cancelProposal.result.events);
});
itSub('TechComm member can add a Fellowship member', async ({helper}) => {
@@ -369,7 +369,7 @@ describeGov('Governance: Technical Committee tests', () => {
itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => {
const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
- const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+ const proposalIndex = Event.Democracy.Proposed.expect(proposeResult.result.events).proposalIndex;
await expect(helper.technicalCommittee.collective.execute(
techcomms.andy,
@@ -422,7 +422,7 @@ describeGov('Governance: Technical Committee tests', () => {
defaultEnactmentMoment,
);
- const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+ const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult.result.events).referendumIndex;
await expect(helper.technicalCommittee.collective.execute(
techcomms.andy,
@@ -439,7 +439,7 @@ describeGov('Governance: Technical Committee tests', () => {
committeeSize,
);
- const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);
+ const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult.result.events);
const proposalIndex = committeeProposedEvent.proposalIndex;
const proposalHash = committeeProposedEvent.proposalHash;
diff --git a/js-packages/tests/xcm/quartz.test.ts b/js-packages/tests/xcm/quartz.test.ts
index 4bb3e556d4..7b846d0fbc 100644
--- a/js-packages/tests/xcm/quartz.test.ts
+++ b/js-packages/tests/xcm/quartz.test.ts
@@ -15,12 +15,229 @@
// along with Unique Network. If not, see .
import type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingShidenPlaygrounds, usingMoonriverPlaygrounds} from '@unique/test-utils/util.js';
-import {QUARTZ_CHAIN, SAFE_XCM_VERSION, XcmTestHelper, SENDER_BUDGET, SENDTO_AMOUNT, SENDBACK_AMOUNT, SHIDEN_DECIMALS, UNQ_DECIMALS} from './xcm.types.js';
+import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingShidenPlaygrounds, usingMoonriverPlaygrounds, usingRelayPlaygrounds, usingKusamaAssetHubPlaygrounds} from '@unique/test-utils/util.js';
+import {QUARTZ_CHAIN, SAFE_XCM_VERSION, XcmTestHelper, SENDER_BUDGET, SENDTO_AMOUNT, SENDBACK_AMOUNT, SHIDEN_DECIMALS, UNQ_DECIMALS, POLKADOT_ASSETHUB_CHAIN, USDT_ASSET_ID, USDT_DECIMALS, ASSET_HUB_PALLET_ASSETS} from './xcm.types.js';
import {hexToString} from '@polkadot/util';
const testHelper = new XcmTestHelper;
+describeXCM('[XCM] Integration test: Exchanging tokens with Relay', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+ let dotDerivativeCollectionId: number;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ const relayLocation = {
+ parents: 1,
+ interior: 'Here',
+ };
+
+ dotDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(relayLocation);
+ if(dotDerivativeCollectionId == null) {
+ const name = 'DOT';
+ const tokenPrefix = 'DOT';
+ const decimals = 10;
+ await helper.getSudo().foreignAssets.register(alice, relayLocation, name, tokenPrefix, {Fungible: decimals});
+
+ dotDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(relayLocation);
+ } else {
+ console.log('Relay foreign collection is already registered');
+ }
+
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ await usingRelayPlaygrounds(async (helper) => {
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+ });
+
+ itSub('Should connect and send DOT to Quartz', async () => {
+ await testHelper.sendDotFromTo(
+ 'relay',
+ 'quartz',
+ randomAccount,
+ randomAccount,
+ SENDTO_AMOUNT,
+ dotDerivativeCollectionId,
+ );
+ });
+
+ itSub('Should connect to Quartz and send DOT back', async () => {
+ await testHelper.sendDotFromTo(
+ 'quartz',
+ 'relay',
+ randomAccount,
+ randomAccount,
+ SENDBACK_AMOUNT,
+ dotDerivativeCollectionId,
+ );
+ });
+
+ itSub('Should not accept reserve transfer of QTZ from Relay', async () => {
+ await testHelper.rejectReserveTransferUNQfrom(
+ alice,
+ 'relay',
+ 'quartz',
+ );
+ });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with AssetHub', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+ let usdtDerivativeCollectionId: number;
+
+ const USDT_NAME = 'USDT';
+ const USDT_SYM = 'USDT';
+
+ let setupAssetHubCall: any;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ const usdtLocation = {
+ parents: 1,
+ interior: {
+ X3: [
+ {
+ Parachain: POLKADOT_ASSETHUB_CHAIN,
+ },
+ {
+ PalletInstance: ASSET_HUB_PALLET_ASSETS,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ],
+ },
+ };
+
+ usdtDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(usdtLocation);
+ if(usdtDerivativeCollectionId == null) {
+ await helper.getSudo().foreignAssets.register(alice, usdtLocation, USDT_NAME, USDT_SYM, {Fungible: USDT_DECIMALS});
+
+ usdtDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(usdtLocation);
+ } else {
+ console.log('USDT collection is already registered');
+ }
+
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ await usingKusamaAssetHubPlaygrounds(async (helper) => {
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ const setSafeXcmVersion = helper.constructApiCall(`api.tx.${helper.xcm.palletName}.forceDefaultXcmVersion`, [SAFE_XCM_VERSION]);
+
+ const isSufficient = true;
+ const minBalance = 10000;
+ const isFrozen = false;
+
+ const createUsdtCall = helper.constructApiCall(
+ 'api.tx.assets.forceCreate', [
+ USDT_ASSET_ID,
+ alice.address,
+ isSufficient,
+ minBalance,
+ ],
+ );
+
+ const setUsdtMetadata = helper.constructApiCall(
+ 'api.tx.assets.forceSetMetadata', [
+ USDT_ASSET_ID,
+ USDT_NAME,
+ USDT_SYM,
+ USDT_DECIMALS,
+ isFrozen,
+ ],
+ );
+
+ setupAssetHubCall = helper.constructApiCall('api.tx.utility.batchAll', [[
+ setSafeXcmVersion,
+ createUsdtCall,
+ setUsdtMetadata,
+ ]]);
+ });
+
+ await usingRelayPlaygrounds(async (helper) => {
+ await helper.getSudo().xcm.send(
+ alice,
+ {
+ V4: {
+ parents: 0,
+ interior: {
+ X1: [{Parachain: POLKADOT_ASSETHUB_CHAIN}],
+ },
+ },
+ },
+ {
+ V4: [
+ {
+ UnpaidExecution: {
+ weightLimit: 'Unlimited',
+ },
+ },
+ {
+ Transact: {
+ originKind: 'Superuser',
+ requireWeightAtMost: {
+ refTime: 8000000000,
+ proofSize: 8000,
+ },
+ call: {
+ encoded: setupAssetHubCall.method.toHex(),
+ },
+ }
+ }
+ ],
+ },
+ );
+ });
+
+ await usingKusamaAssetHubPlaygrounds(async (helper) => {
+ await helper.assets.mint(alice, USDT_ASSET_ID, randomAccount.address, SENDER_BUDGET);
+ });
+ });
+
+ itSub('Should connect and send USDT to Quartz', async () => {
+ await testHelper.sendUsdtFromTo(
+ 'kusamaAssetHub',
+ 'quartz',
+ randomAccount,
+ randomAccount,
+ SENDTO_AMOUNT,
+ usdtDerivativeCollectionId,
+ );
+ });
+
+ itSub('Should connect to Quartz and send USDT back', async () => {
+ await testHelper.sendUsdtFromTo(
+ 'quartz',
+ 'kusamaAssetHub',
+ randomAccount,
+ randomAccount,
+ SENDBACK_AMOUNT,
+ usdtDerivativeCollectionId,
+ );
+ });
+});
+
describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
let alice: IKeyringPair;
let randomAccount: IKeyringPair;
@@ -28,7 +245,9 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
- [randomAccount] = await helper.arrange.createAccounts([0n], alice);
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
// Set the default version to wrap the first message to other chains.
await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
@@ -47,8 +266,8 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
const metadata = {
name: 'Quartz',
symbol: 'QTZ',
- decimals: Number(UNQ_DECIMALS),
- minimalBalance: 1000000000000000000n,
+ decimals: UNQ_DECIMALS,
+ minimalBalance: 1n * 10n ** BigInt(UNQ_DECIMALS),
};
const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
@@ -61,10 +280,6 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
}
await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
});
-
- await usingPlaygrounds(async (helper) => {
- await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
- });
});
itSub('Should connect and send QTZ to Karura', async () => {
@@ -113,12 +328,12 @@ describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
- [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
+ randomAccountQuartz = helper.arrange.createEmptyAccount();
+ await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, SENDER_BUDGET);
+
// 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, randomAccountQuartz.address, SENDER_BUDGET);
});
await usingMoonriverPlaygrounds(async (helper) => {
@@ -207,7 +422,7 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
QTZ_ASSET_ID_ON_SHIDEN,
'Quartz',
'QTZ',
- Number(UNQ_DECIMALS),
+ UNQ_DECIMALS,
);
console.log('2. Register asset location on Shiden');
diff --git a/js-packages/tests/xcm/unique.test.ts b/js-packages/tests/xcm/unique.test.ts
index 80dda3384e..26932fdd7d 100644
--- a/js-packages/tests/xcm/unique.test.ts
+++ b/js-packages/tests/xcm/unique.test.ts
@@ -16,13 +16,230 @@
import type {IKeyringPair} from '@polkadot/types/types';
import config from '../config.js';
-import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds} from '@unique/test-utils/util.js';
+import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingHydraDxPlaygrounds, usingRelayPlaygrounds, usingPolkadotAssetHubPlaygrounds} from '@unique/test-utils/util.js';
import {nToBigInt} from '@polkadot/util';
import {hexToString} from '@polkadot/util';
-import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDBACK_AMOUNT, SENDER_BUDGET, SENDTO_AMOUNT, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper} from './xcm.types.js';
+import {ASSET_HUB_PALLET_ASSETS, ASTAR_DECIMALS, POLKADOT_ASSETHUB_CHAIN, SAFE_XCM_VERSION, SENDBACK_AMOUNT, SENDER_BUDGET, SENDTO_AMOUNT, UNIQUE_CHAIN, UNQ_DECIMALS, USDT_ASSET_ID, USDT_DECIMALS, XcmTestHelper} from './xcm.types.js';
const testHelper = new XcmTestHelper;
+describeXCM('[XCM] Integration test: Exchanging tokens with Relay', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+ let dotDerivativeCollectionId: number;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ const relayLocation = {
+ parents: 1,
+ interior: 'Here',
+ };
+
+ dotDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(relayLocation);
+ if(dotDerivativeCollectionId == null) {
+ const name = 'DOT';
+ const tokenPrefix = 'DOT';
+ const decimals = 10;
+ await helper.getSudo().foreignAssets.register(alice, relayLocation, name, tokenPrefix, {Fungible: decimals});
+
+ dotDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(relayLocation);
+ } else {
+ console.log('Relay foreign collection is already registered');
+ }
+
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ await usingRelayPlaygrounds(async (helper) => {
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+ });
+
+ itSub('Should connect and send DOT to Unique', async () => {
+ await testHelper.sendDotFromTo(
+ 'relay',
+ 'unique',
+ randomAccount,
+ randomAccount,
+ SENDTO_AMOUNT,
+ dotDerivativeCollectionId,
+ );
+ });
+
+ itSub('Should connect to Unique and send DOT back', async () => {
+ await testHelper.sendDotFromTo(
+ 'unique',
+ 'relay',
+ randomAccount,
+ randomAccount,
+ SENDBACK_AMOUNT,
+ dotDerivativeCollectionId,
+ );
+ });
+
+ itSub('Should not accept reserve transfer of UNQ from Relay', async () => {
+ await testHelper.rejectReserveTransferUNQfrom(
+ alice,
+ 'relay',
+ 'unique',
+ );
+ });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with AssetHub', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+ let usdtDerivativeCollectionId: number;
+
+ const USDT_NAME = 'USDT';
+ const USDT_SYM = 'USDT';
+
+ let setupAssetHubCall: any;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ const usdtLocation = {
+ parents: 1,
+ interior: {
+ X3: [
+ {
+ Parachain: POLKADOT_ASSETHUB_CHAIN,
+ },
+ {
+ PalletInstance: ASSET_HUB_PALLET_ASSETS,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ],
+ },
+ };
+
+ usdtDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(usdtLocation);
+ if(usdtDerivativeCollectionId == null) {
+ await helper.getSudo().foreignAssets.register(alice, usdtLocation, USDT_NAME, USDT_SYM, {Fungible: USDT_DECIMALS});
+
+ usdtDerivativeCollectionId = await helper.foreignAssets.foreignCollectionId(usdtLocation);
+ } else {
+ console.log('USDT collection is already registered');
+ }
+
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ await usingPolkadotAssetHubPlaygrounds(async (helper) => {
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+
+ const setSafeXcmVersion = helper.constructApiCall(`api.tx.${helper.xcm.palletName}.forceDefaultXcmVersion`, [SAFE_XCM_VERSION]);
+
+ const isSufficient = true;
+ const minBalance = 10000;
+ const isFrozen = false;
+
+ const createUsdtCall = helper.constructApiCall(
+ 'api.tx.assets.forceCreate', [
+ USDT_ASSET_ID,
+ alice.address,
+ isSufficient,
+ minBalance,
+ ],
+ );
+
+ const setUsdtMetadata = helper.constructApiCall(
+ 'api.tx.assets.forceSetMetadata', [
+ USDT_ASSET_ID,
+ USDT_NAME,
+ USDT_SYM,
+ USDT_DECIMALS,
+ isFrozen,
+ ],
+ );
+
+ setupAssetHubCall = helper.constructApiCall('api.tx.utility.batchAll', [[
+ setSafeXcmVersion,
+ createUsdtCall,
+ setUsdtMetadata,
+ ]]);
+ });
+
+ await usingRelayPlaygrounds(async (helper) => {
+ await helper.getSudo().xcm.send(
+ alice,
+ {
+ V4: {
+ parents: 0,
+ interior: {
+ X1: [{Parachain: POLKADOT_ASSETHUB_CHAIN}],
+ },
+ },
+ },
+ {
+ V4: [
+ {
+ UnpaidExecution: {
+ weightLimit: 'Unlimited',
+ },
+ },
+ {
+ Transact: {
+ originKind: 'Superuser',
+ requireWeightAtMost: {
+ refTime: 8000000000,
+ proofSize: 8000,
+ },
+ call: {
+ encoded: setupAssetHubCall.method.toHex(),
+ },
+ }
+ }
+ ],
+ },
+ );
+ });
+
+ await usingPolkadotAssetHubPlaygrounds(async (helper) => {
+ await helper.assets.mint(alice, USDT_ASSET_ID, randomAccount.address, SENDER_BUDGET);
+ });
+ });
+
+ itSub('Should connect and send USDT to Unique', async () => {
+ await testHelper.sendUsdtFromTo(
+ 'polkadotAssetHub',
+ 'unique',
+ randomAccount,
+ randomAccount,
+ SENDTO_AMOUNT,
+ usdtDerivativeCollectionId,
+ );
+ });
+
+ itSub('Should connect to Unique and send USDT back', async () => {
+ await testHelper.sendUsdtFromTo(
+ 'unique',
+ 'polkadotAssetHub',
+ randomAccount,
+ randomAccount,
+ SENDBACK_AMOUNT,
+ usdtDerivativeCollectionId,
+ );
+ });
+});
+
describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
let alice: IKeyringPair;
let randomAccount: IKeyringPair;
@@ -35,6 +252,8 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
// 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 usingAcalaPlaygrounds(async (helper) => {
@@ -50,7 +269,7 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
const metadata = {
name: 'Unique Network',
symbol: 'UNQ',
- decimals: Number(UNQ_DECIMALS),
+ decimals: UNQ_DECIMALS,
minimalBalance: 1250_000_000_000_000_000n,
};
const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
@@ -63,10 +282,6 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
}
await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
});
-
- await usingPlaygrounds(async (helper) => {
- await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
- });
});
itSub('Should connect and send UNQ to Acala', async () => {
@@ -193,12 +408,12 @@ describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
- [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);
+ randomAccountUnique = helper.arrange.createEmptyAccount();
+ await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);
+
// 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, randomAccountUnique.address, SENDER_BUDGET);
});
await usingMoonbeamPlaygrounds(async (helper) => {
@@ -258,7 +473,7 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {
const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar
// Unique -> Astar
- const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
+ const astarInitialBalance = 1n * 10n ** BigInt(ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar
before(async () => {
@@ -287,7 +502,7 @@ describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {
UNQ_ASSET_ID_ON_ASTAR,
'Unique Network',
'UNQ',
- Number(UNQ_DECIMALS),
+ UNQ_DECIMALS,
);
console.log('2. Register asset location on Astar');
diff --git a/js-packages/tests/xcm/xcm.types.ts b/js-packages/tests/xcm/xcm.types.ts
index 9c92f5c473..3499f45d32 100644
--- a/js-packages/tests/xcm/xcm.types.ts
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -1,11 +1,12 @@
import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
+import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingHydraDxPlaygrounds, usingKaruraPlaygrounds, usingKusamaAssetHubPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingPolkadotAssetHubPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '@unique/test-utils/util.js';
import {DevUniqueHelper, Event} from '@unique/test-utils';
import { AcalaHelper, AstarHelper } from '@unique/test-utils/xcm/index.js';
-import { IEvent } from '@unique-nft/playgrounds/types.js';
+import { IEvent, ITransactionResult } from '@unique-nft/playgrounds/types.js';
+import { ChainHelperBase } from '@unique-nft/playgrounds';
export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
-export const POLKADOT_ASSETHUB_CHAIN = +(process.env.RELAY_ASSETHUB_URL || 1000);
+export const POLKADOT_ASSETHUB_CHAIN = +(process.env.RELAY_ASSETHUB_ID || 1000);
export const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);
export const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);
export const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);
@@ -25,8 +26,8 @@ export const KUSAMA_ASSETHUB_DECIMALS = 12;
export const KARURA_DECIMALS = 12;
export const SHIDEN_DECIMALS = 18n;
-export const ASTAR_DECIMALS = 18n;
-export const UNQ_DECIMALS = 18n;
+export const ASTAR_DECIMALS = 18;
+export const UNQ_DECIMALS = 18;
export const maxWaitBlocks = 50;
@@ -44,6 +45,8 @@ export const NETWORKS = {
unique: usingPlaygrounds,
quartz: usingPlaygrounds,
relay: usingRelayPlaygrounds,
+ kusamaAssetHub: usingKusamaAssetHubPlaygrounds,
+ polkadotAssetHub: usingPolkadotAssetHubPlaygrounds,
acala: usingAcalaPlaygrounds,
astar: usingAstarPlaygrounds,
polkadex: usingPolkadexPlaygrounds,
@@ -65,6 +68,9 @@ export function mapToChainId(networkName: keyof typeof NETWORKS): number {
return QUARTZ_CHAIN;
case 'relay':
throw new Error('Relay chain has no para ID');
+ case 'kusamaAssetHub':
+ case 'polkadotAssetHub':
+ return POLKADOT_ASSETHUB_CHAIN;
case 'acala':
return ACALA_CHAIN;
case 'astar':
@@ -93,11 +99,47 @@ export function mapToChainLocation(networkName: keyof typeof NETWORKS) {
};
}
+export const USDT_ASSET_ID = 1984;
+export const USDT_DECIMALS = 6;
+export const ASSET_HUB_PALLET_ASSETS = 50;
+
+export function mapToUsdtLocation(networkName: keyof typeof NETWORKS) {
+ const basicInteriors = [
+ {
+ PalletInstance: ASSET_HUB_PALLET_ASSETS,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ];
+
+ if (networkName === 'kusamaAssetHub' || networkName === 'polkadotAssetHub') {
+ return {
+ parents: 0,
+ interior: {
+ X2: basicInteriors,
+ },
+ };
+ } else {
+ return {
+ parents: 1,
+ interior: {
+ X3: [
+ {
+ Parachain: POLKADOT_ASSETHUB_CHAIN,
+ },
+ ...basicInteriors,
+ ],
+ },
+ };
+ }
+}
+
export function getDevPlayground(name: NetworkNames) {
return NETWORKS[name];
}
-export const TRANSFER_AMOUNT = 2000000n * 10n ** UNQ_DECIMALS;
+export const TRANSFER_AMOUNT = 2000000n * 10n ** BigInt(UNQ_DECIMALS);
export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
export const SENDTO_AMOUNT = TRANSFER_AMOUNT;
export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
@@ -163,8 +205,15 @@ export class XcmTestHelper {
const otherChainPlayground = getDevPlayground(sendFrom);
return await otherChainPlayground(async (helper) => {
- const destination = {V4: mapToChainLocation(sendTo)};
- const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [destination, program]);
+ const destination = sendFrom === 'relay'
+ ? {
+ V4: {
+ parents: 0,
+ interior: {X1: [{Parachain: mapToChainId(sendTo)}]},
+ },
+ }
+ : {V4: mapToChainLocation(sendTo)};
+ const xcmSend = helper.constructApiCall(`api.tx.${helper.xcm.palletName}.send`, [destination, program]);
if('getSudo' in helper) {
// can't use `getSudo` here because of types issues.
@@ -177,7 +226,7 @@ export class XcmTestHelper {
'api.tx.sudo.sudo',
[xcmSend],
);
- const messageSent = Event.XcmpQueue.XcmpMessageSent.expect(sendResult);
+ const messageSent = await this.#expectSentEvent(helper, sendFrom, sendTo, sendResult);
return messageSent.messageHash;
} else if ('fastDemocracy' in helper) {
// Needed to bypass the call filter.
@@ -214,45 +263,63 @@ export class XcmTestHelper {
});
}
- async #sendTokens(
+ async #expectSentEvent(
+ fromHelper: ChainHelperBase,
+ from: keyof typeof NETWORKS,
+ to: keyof typeof NETWORKS,
+ txResult: ITransactionResult,
+ ) {
+ // FIXME
+ // WORKAROUND: sometimes a part of the events collected directly from the extrinsic
+ // is lost somehow. Let's query all of them from the block.
+ const apiAt = await fromHelper.getApi().at(txResult.blockHash);
+ const eventRecords = (await apiAt.query.system.events()).toArray();
+ const events = fromHelper.eventHelper.extractPhasicEvents(eventRecords);
+
+ if (from === 'relay') {
+ return Event.XcmPallet.Sent.expect(events);
+ } else if (to === 'relay' || from === 'kusamaAssetHub' || from === 'polkadotAssetHub') {
+ return Event.PolkadotXcm.Sent.expect(events);
+ } else {
+ return Event.XcmpQueue.XcmpMessageSent.expect(events);
+ }
+ }
+
+ async #sendTokens({
+ from,
+ to,
+ fromAccount,
+ toAccount,
+ assetId,
+ amount,
+ decimals,
+ getAssetBalanceOnUnique,
+ setMessageHash,
+ }: {
from: keyof typeof NETWORKS,
to: keyof typeof NETWORKS,
fromAccount: IKeyringPair,
toAccount: IKeyringPair,
+ assetId: any,
amount: bigint,
+ decimals: number,
+ getAssetBalanceOnUnique: (helper: DevUniqueHelper) => Promise,
setMessageHash: (messageHash: any) => void,
- ) {
+ }) {
let isFromUnique = from === 'unique' || from === 'quartz';
- let isToUnique = to === 'unique' || to === 'quartz';
const fromPlayground = getDevPlayground(from);
await fromPlayground(async (helper) => {
- let assetId: any;
- if (isFromUnique) {
- assetId = {
- parents: 0,
- interior: 'here',
- };
- } else if (isToUnique) {
- assetId = mapToChainLocation(to);
- } else {
- throw new Error('sendUnqFromTo: either `from` or `to` MUST point to a Unique chain');
- }
-
const getRandomAccountBalance = async (): Promise => {
if (!isFromUnique) {
return 0n;
}
- if ('getSubstrate' in helper.balance) {
- return await helper.balance.getSubstrate(fromAccount.address);
- } else {
- return await helper.balance.getEthereum(fromAccount.address);
- }
+ return await getAssetBalanceOnUnique(helper as DevUniqueHelper);
};
- const unqBalanceBefore = await getRandomAccountBalance();
+ const balanceBefore = await getRandomAccountBalance();
let beneficiaryAccount: any;
if (this._isAddress20FormatFor(to)) {
@@ -315,7 +382,9 @@ export class XcmTestHelper {
'Unlimited',
);
} else {
- const destination = {V4: mapToChainLocation(to)};
+ const destination = from === 'relay'
+ ? {V4: {parents: 0, interior: {X1: [{Parachain: mapToChainId(to)}]}}}
+ : {V4: mapToChainLocation(to)};
const beneficiary = {
V4: {
@@ -336,20 +405,20 @@ export class XcmTestHelper {
);
}
- const messageSent = Event.XcmpQueue.XcmpMessageSent.expect(transferResult);
+ const messageSent = await this.#expectSentEvent(helper, from, to, transferResult);
- const unqBalanceAfter = await getRandomAccountBalance();
+ const balanceAfter = await getRandomAccountBalance();
if (isFromUnique) {
- const unqBalanceDiff = unqBalanceBefore - unqBalanceAfter;
- const unqFees = unqBalanceDiff - amount;
- const unqMinFees = 0n;
- const unqMaxFees = 2n * 10n ** UNQ_DECIMALS;
+ const balanceDiff = balanceBefore - balanceAfter;
+ const fees = balanceDiff - amount;
+ const minFees = 0n;
+ const maxFees = 2n * 10n ** BigInt(decimals);
- console.log('[%s -> %s] transaction fees: %s UNQ/QTZ', from, to, helper.util.bigIntToDecimals(unqFees));
+ console.log('[%s -> %s] transaction fees: %s asset tokens', from, to, helper.util.bigIntToDecimals(fees, decimals));
expect(
- unqMinFees < unqFees && unqFees <= unqMaxFees,
- `invalid UNQ/QTZ fees when transferring from Unique/Quartz: ${unqFees}`
+ minFees <= fees && fees <= maxFees,
+ `invalid asset fees when transferring from ${from}: ${fees}`
).to.be.true;
}
@@ -357,13 +426,21 @@ export class XcmTestHelper {
});
}
- async #awaitTokens(
+ async #awaitTokens({
+ from,
+ to,
+ amount,
+ decimals,
+ getAssetBalanceOnUnique,
+ getMessageHash,
+ }: {
from: keyof typeof NETWORKS,
to: keyof typeof NETWORKS,
- getMessageHash: () => any,
- account: string,
amount: bigint,
- ) {
+ decimals: number,
+ getAssetBalanceOnUnique: (helper: DevUniqueHelper) => Promise,
+ getMessageHash: () => any,
+ }) {
const toPlayground = getDevPlayground(to);
let isToUnique = to === 'unique' || to === 'quartz';
@@ -373,14 +450,10 @@ export class XcmTestHelper {
return 0n;
}
- if ('getSubstrate' in helper.balance) {
- return await helper.balance.getSubstrate(account);
- } else {
- return await helper.balance.getEthereum(account);
- }
+ return await getAssetBalanceOnUnique(helper as DevUniqueHelper);
};
- const unqBalanceBefore = await getRandomAccountBalance();
+ const balanceBefore = await getRandomAccountBalance();
const collectedEventData = await this.#collectProcessedMsgsEvents(
helper,
@@ -397,22 +470,124 @@ export class XcmTestHelper {
`no 'MessageQueue.Processed' event was found on ${to}`,
).to.be.true;
- const unqBalanceAfter = await getRandomAccountBalance();
+ const balanceAfter = await getRandomAccountBalance();
if (isToUnique) {
- const unqBalanceDiff = unqBalanceAfter - unqBalanceBefore;
- const unqFees = unqBalanceDiff - amount;
+ const balanceDiff = balanceAfter - balanceBefore;
+ const fees = balanceDiff - amount;
- console.log('[%s -> %s] transaction fees: %s UNQ/QTZ', from, to, helper.util.bigIntToDecimals(unqFees));
+ console.log('[%s -> %s] transaction fees: %s asset tokens', from, to, helper.util.bigIntToDecimals(fees, decimals));
expect(
- unqFees === 0n,
- `invalid UNQ/QTZ fees when receiving to ${to}: ${unqFees}`
+ fees === 0n,
+ `invalid asset fees when receiving to ${to}: ${fees}`
).to.be.true;
}
});
}
+ async sendDotFromTo(
+ from: keyof typeof NETWORKS,
+ to: keyof typeof NETWORKS,
+ randomAccountOnFrom: IKeyringPair,
+ randomAccountOnTo: IKeyringPair,
+ amount: bigint,
+ dotDerivativeCollectionId: number,
+ ) {
+ let messageHash: any = null;
+
+ let isFromUnique = from === 'unique' || from === 'quartz';
+
+ let assetId: any;
+ if (isFromUnique) {
+ assetId = {
+ parents: 1,
+ interior: 'here',
+ };
+ } else {
+ assetId = {
+ parents: 0,
+ interior: 'here',
+ };
+ };
+
+ await Promise.all([
+ this.#sendTokens({
+ from,
+ to,
+ fromAccount: randomAccountOnFrom,
+ toAccount: randomAccountOnTo,
+ assetId,
+ amount,
+ decimals: UNQ_DECIMALS,
+ getAssetBalanceOnUnique: async (helper: DevUniqueHelper) => {
+ return await helper.ft.getBalance(
+ dotDerivativeCollectionId,
+ {Substrate: randomAccountOnFrom.address},
+ );
+ },
+ setMessageHash: (hash) => messageHash = hash,
+ }),
+ this.#awaitTokens({
+ from,
+ to,
+ amount,
+ decimals: UNQ_DECIMALS,
+ getAssetBalanceOnUnique: async (helper: DevUniqueHelper) => {
+ return await helper.ft.getBalance(
+ dotDerivativeCollectionId,
+ {Substrate: randomAccountOnTo.address},
+ );
+ },
+ getMessageHash: () => messageHash,
+ }),
+ ]);
+ }
+
+ async sendUsdtFromTo(
+ from: keyof typeof NETWORKS,
+ to: keyof typeof NETWORKS,
+ randomAccountOnFrom: IKeyringPair,
+ randomAccountOnTo: IKeyringPair,
+ amount: bigint,
+ usdtDerivativeCollectionId: number,
+ ) {
+ let messageHash: any = null;
+ let assetId = mapToUsdtLocation(from);
+
+ await Promise.all([
+ this.#sendTokens({
+ from,
+ to,
+ fromAccount: randomAccountOnFrom,
+ toAccount: randomAccountOnTo,
+ assetId,
+ amount,
+ decimals: USDT_DECIMALS,
+ getAssetBalanceOnUnique: async (helper: DevUniqueHelper) => {
+ return await helper.ft.getBalance(
+ usdtDerivativeCollectionId,
+ {Substrate: randomAccountOnFrom.address},
+ );
+ },
+ setMessageHash: (hash) => messageHash = hash,
+ }),
+ this.#awaitTokens({
+ from,
+ to,
+ amount,
+ decimals: UNQ_DECIMALS,
+ getAssetBalanceOnUnique: async (helper: DevUniqueHelper) => {
+ return await helper.ft.getBalance(
+ usdtDerivativeCollectionId,
+ {Substrate: randomAccountOnTo.address},
+ );
+ },
+ getMessageHash: () => messageHash,
+ }),
+ ]);
+ }
+
async sendUnqFromTo(
from: keyof typeof NETWORKS,
to: keyof typeof NETWORKS,
@@ -422,22 +597,42 @@ export class XcmTestHelper {
) {
let messageHash: any = null;
+ let isFromUnique = from === 'unique' || from === 'quartz';
+
+ let assetId: any;
+ if (isFromUnique) {
+ assetId = {
+ parents: 0,
+ interior: 'here',
+ };
+ } else {
+ assetId = mapToChainLocation(to);
+ };
+
await Promise.all([
- this.#sendTokens(
+ this.#sendTokens({
from,
to,
- randomAccountOnFrom,
- randomAccountOnTo,
+ fromAccount: randomAccountOnFrom,
+ toAccount: randomAccountOnTo,
+ assetId,
amount,
- (hash) => messageHash = hash,
- ),
- this.#awaitTokens(
+ decimals: UNQ_DECIMALS,
+ getAssetBalanceOnUnique: async (helper: DevUniqueHelper) => {
+ return await helper.balance.getSubstrate(randomAccountOnFrom.address);
+ },
+ setMessageHash: (hash) => messageHash = hash,
+ }),
+ this.#awaitTokens({
from,
to,
- () => messageHash,
- randomAccountOnTo.address,
amount,
- ),
+ decimals: UNQ_DECIMALS,
+ getAssetBalanceOnUnique: async (helper: DevUniqueHelper) => {
+ return await helper.balance.getSubstrate(randomAccountOnTo.address);
+ },
+ getMessageHash: () => messageHash,
+ }),
]);
}
@@ -446,7 +641,7 @@ export class XcmTestHelper {
otherChain: keyof typeof NETWORKS,
uniqueChain: UniqueChain,
) {
- const otherChainBalance = 10000n * (10n ** UNQ_DECIMALS);
+ const otherChainBalance = 10000n * 10n ** BigInt(UNQ_DECIMALS);
let randomAccount: IKeyringPair;
let maliciousXcmProgram: any;
@@ -515,13 +710,16 @@ export class XcmTestHelper {
await Promise.all([
sendGoodProgram(),
- this.#awaitTokens(
- otherChain,
- uniqueChain,
- () => messageHash,
- randomAccount!.address,
- otherChainBalance,
- )
+ this.#awaitTokens({
+ from: otherChain,
+ to: uniqueChain,
+ amount: otherChainBalance,
+ decimals: UNQ_DECIMALS,
+ getAssetBalanceOnUnique: async (helper) => {
+ return await helper.balance.getSubstrate(randomAccount!.address);
+ },
+ getMessageHash: () => messageHash,
+ })
]);
await usingPlaygrounds(async (helper) => {
@@ -535,7 +733,7 @@ export class XcmTestHelper {
otherChain: keyof typeof NETWORKS,
uniqueChain: UniqueChain,
) {
- const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+ const testAmount = 10_000n * 10n ** BigInt(UNQ_DECIMALS);
let randomAccount: IKeyringPair;
let messageHash: any = null;
@@ -627,26 +825,4 @@ export class XcmTestHelper {
this.#awaitMaliciousProgramRejection(() => messageHash),
]);
}
-
- async registerRelayNativeTokenOnUnique(alice: IKeyringPair) {
- return await usingPlaygrounds(async (helper) => {
- const relayLocation = {
- parents: 1,
- interior: 'Here',
- };
-
- const relayCollectionId = await helper.foreignAssets.foreignCollectionId(relayLocation);
- if(relayCollectionId == null) {
- const name = 'DOT';
- const tokenPrefix = 'DOT';
- const decimals = 10;
- await helper.getSudo().foreignAssets.register(alice, relayLocation, name, tokenPrefix, {Fungible: decimals});
-
- return await helper.foreignAssets.foreignCollectionId(relayLocation);
- } else {
- console.log('Relay foreign collection is already registered');
- return relayCollectionId;
- }
- });
- }
}