difftreelog
Merge pull request #801 from UniqueNetwork/fix/xcm-qtz-unq-tests
in: master
Fix xcm
10 files changed
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -161,9 +161,7 @@
fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {
log::trace!(target: "fassets::get_currency_id", "call");
- Some(AssetIds::ForeignAssetId(
- Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),
- ))
+ Pallet::<T>::location_to_currency_ids(multi_location).map(|id| AssetIds::ForeignAssetId(id))
}
}
runtime/common/config/xcm/foreignassets.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/foreignassets.rs
+++ b/runtime/common/config/xcm/foreignassets.rs
@@ -18,7 +18,7 @@
traits::{Contains, Get, fungibles},
parameter_types,
};
-use sp_runtime::traits::{Zero, Convert};
+use sp_runtime::traits::Convert;
use xcm::v1::{Junction::*, MultiLocation, Junctions::*};
use xcm::latest::MultiAsset;
use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId};
@@ -38,16 +38,16 @@
pub CheckingAccount: AccountId = PolkadotXcm::check_account();
}
-/// Allow checking in assets that have issuance > 0.
-pub struct NonZeroIssuance<AccountId, ForeignAssets>(PhantomData<(AccountId, ForeignAssets)>);
+/// No teleports are allowed
+pub struct NoTeleports<AccountId, ForeignAssets>(PhantomData<(AccountId, ForeignAssets)>);
impl<AccountId, ForeignAssets> Contains<<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId>
- for NonZeroIssuance<AccountId, ForeignAssets>
+ for NoTeleports<AccountId, ForeignAssets>
where
ForeignAssets: fungibles::Inspect<AccountId>,
{
- fn contains(id: &<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId) -> bool {
- !ForeignAssets::total_issuance(*id).is_zero()
+ fn contains(_id: &<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId) -> bool {
+ false
}
}
@@ -84,7 +84,7 @@
Some(AssetIds::ForeignAssetId(foreign_asset_id)) => {
ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(foreign_asset_id))
}
- _ => ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(0)),
+ _ => Err(()),
}
}
@@ -132,9 +132,8 @@
LocationToAccountId,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
- // We only want to allow teleports of known assets. We use non-zero issuance as an indication
- // that this asset is known.
- NonZeroIssuance<AccountId, ForeignAssets>,
+ // No teleports are allowed
+ NoTeleports<AccountId, ForeignAssets>,
// The account to use for tracking teleports.
CheckingAccount,
>;
runtime/common/config/xcm/mod.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/mod.rs
+++ b/runtime/common/config/xcm/mod.rs
@@ -186,6 +186,9 @@
TransferReserveAsset { dest: dst, .. } => {
allowed |= allowed_locations.contains(dst);
}
+ InitiateReserveWithdraw { reserve: dst, .. } => {
+ allowed |= allowed_locations.contains(dst);
+ }
_ => {}
});
tests/src/config.tsdiffbeforeafterboth--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -25,6 +25,8 @@
moonbeamUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',
moonriverUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',
westmintUrl: process.env.westmintUrl || 'ws://127.0.0.1:9948',
+ statemineUrl: process.env.statemineUrl || 'ws://127.0.0.1:9948',
+ statemintUrl: process.env.statemintUrl || 'ws://127.0.0.1:9948',
};
export default config;
tests/src/util/index.tsdiffbeforeafterboth--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -11,7 +11,7 @@
import config from '../config';
import {ChainHelperBase} from './playgrounds/unique';
import {ILogger} from './playgrounds/types';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';
+import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper} from './playgrounds/unique.dev';
chai.use(chaiAsPromised);
chai.use(chaiSubset);
@@ -65,6 +65,14 @@
return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
};
+export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
+};
+
+export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+ return usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
+};
+
export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
};
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -118,7 +118,16 @@
}
}
-export class DevRelayHelper extends RelayHelper {}
+export class DevRelayHelper extends RelayHelper {
+ wait: WaitGroup;
+
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.helperBase = options.helperBase ?? DevRelayHelper;
+
+ super(logger, options);
+ this.wait = new WaitGroup(this);
+ }
+}
export class DevWestmintHelper extends WestmintHelper {
wait: WaitGroup;
@@ -131,12 +140,17 @@
}
}
+export class DevStatemineHelper extends DevWestmintHelper {}
+
+export class DevStatemintHelper extends DevWestmintHelper {}
+
export class DevMoonbeamHelper extends MoonbeamHelper {
account: MoonbeamAccountGroup;
wait: WaitGroup;
constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
options.helperBase = options.helperBase ?? DevMoonbeamHelper;
+ options.notePreimagePallet = options.notePreimagePallet ?? 'democracy';
super(logger, options);
this.account = new MoonbeamAccountGroup(this);
@@ -144,7 +158,12 @@
}
}
-export class DevMoonriverHelper extends DevMoonbeamHelper {}
+export class DevMoonriverHelper extends DevMoonbeamHelper {
+ constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+ options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
+ super(logger, options);
+ }
+}
export class DevAcalaHelper extends AcalaHelper {
wait: WaitGroup;
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth2740 this.palletName = palletName;2740 this.palletName = palletName;2741 }2741 }274227422743 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2743 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {2744 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2744 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);2745 }2745 }27462747 async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {2748 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);2749 }27502751 async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint) {2752 const destination = {2753 V1: {2754 parents: 0,2755 interior: {2756 X1: {2757 Parachain: destinationParaId,2758 },2759 },2760 },2761 };27622763 const beneficiary = {2764 V1: {2765 parents: 0,2766 interior: {2767 X1: {2768 AccountId32: {2769 network: 'Any',2770 id: targetAccount,2771 },2772 },2773 },2774 },2775 };27762777 const assets = {2778 V1: [2779 {2780 id: {2781 Concrete: {2782 parents: 0,2783 interior: 'Here',2784 },2785 },2786 fun: {2787 Fungible: amount,2788 },2789 },2790 ],2791 };27922793 const feeAssetItem = 0;27942795 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);2796 }2746}2797}274727982748class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2799class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2749 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2800 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {2750 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2801 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2751 }2802 }275228032753 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2804 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {2754 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2805 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2755 }2806 }275628072757 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2808 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {2758 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2809 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2759 }2810 }2760}2811}2823}2874}282428752825class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2876class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2877 notePreimagePallet: string;28782879 constructor(helper: MoonbeamHelper, options: {[key: string]: any} = {}) {2880 super(helper);2881 this.notePreimagePallet = options.notePreimagePallet;2882 }28832826 async notePreimage(signer: TSigner, encodedProposal: string) {2884 async notePreimage(signer: TSigner, encodedProposal: string) {2827 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2885 await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);2828 }2886 }282928872830 externalProposeMajority(proposalHash: string) {2888 externalProposeMajority(proposal: any) {2831 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2889 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);2832 }2890 }283328912834 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2892 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2857 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2915 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2858 }2916 }285929172860 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2918 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {2861 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2919 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2862 }2920 }286329212917}2975}291829762919export class RelayHelper extends XcmChainHelper {2977export class RelayHelper extends XcmChainHelper {2978 balance: SubstrateBalanceGroup<RelayHelper>;2920 xcm: XcmGroup<RelayHelper>;2979 xcm: XcmGroup<RelayHelper>;292129802922 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2981 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2923 super(logger, options.helperBase ?? RelayHelper);2982 super(logger, options.helperBase ?? RelayHelper);292429832984 this.balance = new SubstrateBalanceGroup(this);2925 this.xcm = new XcmGroup(this, 'xcmPallet');2985 this.xcm = new XcmGroup(this, 'xcmPallet');2926 }2986 }2927}2987}2960 this.assetManager = new MoonbeamAssetManagerGroup(this);3020 this.assetManager = new MoonbeamAssetManagerGroup(this);2961 this.assets = new AssetsGroup(this);3021 this.assets = new AssetsGroup(this);2962 this.xTokens = new XTokensGroup(this);3022 this.xTokens = new XTokensGroup(this);2963 this.democracy = new MoonbeamDemocracyGroup(this);3023 this.democracy = new MoonbeamDemocracyGroup(this, options);2964 this.collective = {3024 this.collective = {2965 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),3025 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2966 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),3026 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),tests/src/xcm/xcmOpal.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmOpal.test.ts
+++ b/tests/src/xcm/xcmOpal.test.ts
@@ -31,6 +31,7 @@
const ASSET_METADATA_DESCRIPTION = 'USDT';
const ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const RELAY_DECIMALS = 12;
const WESTMINT_DECIMALS = 12;
const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
@@ -147,9 +148,8 @@
};
const feeAssetItem = 0;
- const weightLimit = 5_000_000_000;
- await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);
+ await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
});
});
@@ -202,16 +202,15 @@
};
const feeAssetItem = 0;
- const weightLimit = 5000000000;
balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
- await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, weightLimit);
+ await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
// common good parachain take commission in it native token
console.log(
- 'Opal to Westmint transaction fees on Westmint: %s WND',
+ '[Westmint -> Opal] transaction fees on Westmint: %s WND',
helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),
);
expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
@@ -227,18 +226,19 @@
balanceOpalAfter = await helper.balance.getSubstrate(alice.address);
- // commission has not paid in USDT token
- expect(free == TRANSFER_AMOUNT).to.be.true;
console.log(
- 'Opal to Westmint transaction fees on Opal: %s USDT',
- helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free),
+ '[Westmint -> Opal] transaction fees on Opal: %s USDT',
+ helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),
);
+ console.log(
+ '[Westmint -> Opal] transaction fees on Opal: %s OPL',
+ helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore),
+ );
+
+ // commission has not paid in USDT token
+ expect(free == TRANSFER_AMOUNT).to.be.true;
// ... and parachain native token
expect(balanceOpalAfter == balanceOpalBefore).to.be.true;
- console.log(
- 'Opal to Westmint transaction fees on Opal: %s WND',
- helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),
- );
});
itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {
@@ -276,9 +276,8 @@
];
const feeItem = 1;
- const destWeight = 500000000000;
- await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, destWeight);
+ await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
// the commission has been paid in parachain native token
balanceOpalFinal = await helper.balance.getSubstrate(alice.address);
@@ -339,9 +338,8 @@
};
const feeAssetItem = 0;
- const weightLimit = 5_000_000_000;
- await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, weightLimit);
+ await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
});
await helper.wait.newBlocks(3);
@@ -363,20 +361,23 @@
});
itSub('Should connect and send Relay token back', async ({helper}) => {
+ let relayTokenBalanceBefore: bigint;
+ let relayTokenBalanceAfter: bigint;
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+ });
+
const destination = {
V1: {
parents: 1,
- interior: {X2: [
- {
- Parachain: STATEMINE_CHAIN,
- },
- {
+ interior: {
+ X1:{
AccountId32: {
network: 'Any',
id: bob.addressRaw,
},
},
- ]},
+ },
},
};
@@ -390,11 +391,19 @@
];
const feeItem = 0;
- const destWeight = 500000000000;
- await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, destWeight);
+ await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
balanceBobFinal = await helper.balance.getSubstrate(bob.address);
- console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobFinal);
+ console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ await helper.wait.newBlocks(10);
+ relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+ const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+ console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+ expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+ });
});
});
tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -17,21 +17,430 @@
import {IKeyringPair} from '@polkadot/types/types';
import {blake2AsHex} from '@polkadot/util-crypto';
import config from '../config';
-import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util';
+import {XcmV2TraitsError} from '../interfaces';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds} from '../util';
const QUARTZ_CHAIN = 2095;
+const STATEMINE_CHAIN = 1000;
const KARURA_CHAIN = 2000;
const MOONRIVER_CHAIN = 2023;
+const STATEMINE_PALLET_INSTANCE = 50;
+
const relayUrl = config.relayUrl;
+const statemineUrl = config.statemineUrl;
const karuraUrl = config.karuraUrl;
const moonriverUrl = config.moonriverUrl;
+const RELAY_DECIMALS = 12;
+const STATEMINE_DECIMALS = 12;
const KARURA_DECIMALS = 12;
const TRANSFER_AMOUNT = 2000000000000000000000000n;
+const FUNDING_AMOUNT = 3_500_000_0000_000_000n;
+
+const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
+ let balanceStmnBefore: bigint;
+ let balanceStmnAfter: bigint;
+
+ let balanceQuartzBefore: bigint;
+ let balanceQuartzAfter: bigint;
+ let balanceQuartzFinal: bigint;
+
+ let balanceBobBefore: bigint;
+ let balanceBobAfter: bigint;
+ let balanceBobFinal: bigint;
+
+ let balanceBobRelayTokenBefore: bigint;
+ let balanceBobRelayTokenAfter: bigint;
+
+
+ before(async () => {
+ await usingPlaygrounds(async (_helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor
+ });
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ // Fund accounts on Statemine(t)
+ await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+ await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
+ });
+
+ await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+ const sovereignFundingAmount = 3_500_000_000n;
+
+ await helper.assets.create(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ );
+ await helper.assets.setMetadata(
+ alice,
+ USDT_ASSET_ID,
+ USDT_ASSET_METADATA_NAME,
+ USDT_ASSET_METADATA_DESCRIPTION,
+ USDT_ASSET_METADATA_DECIMALS,
+ );
+ await helper.assets.mint(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_AMOUNT,
+ );
+
+ // funding parachain sovereing account on Statemine(t).
+ // The sovereign account should be created before any action
+ // (the assets pallet on Statemine(t) check if the sovereign account exists)
+ const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);
+ await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
+ });
+
+
+ await usingPlaygrounds(async (helper) => {
+ const location = {
+ V1: {
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: STATEMINE_CHAIN,
+ },
+ {
+ PalletInstance: STATEMINE_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
+ },
+ };
+
+ const metadata =
+ {
+ name: USDT_ASSET_ID,
+ symbol: USDT_ASSET_METADATA_NAME,
+ decimals: USDT_ASSET_METADATA_DECIMALS,
+ minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ };
+ await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+ balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);
+ });
+
+
+ // Providing the relay currency to the quartz sender account
+ // (fee for USDT XCM are paid in relay tokens)
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ const destination = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ }};
+
+ const beneficiary = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ }},
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: TRANSFER_AMOUNT_RELAY,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+ });
+
+ });
+
+ itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {
+ await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+ const dest = {
+ V1: {
+ parents: 1,
+ interior: {X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ }};
+
+ const beneficiary = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ }},
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: {
+ X2: [
+ {
+ PalletInstance: STATEMINE_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
+ },
+ },
+ fun: {
+ Fungible: TRANSFER_AMOUNT,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+ await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+ balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+ // common good parachain take commission in it native token
+ console.log(
+ '[Statemine -> Quartz] transaction fees on Statemine: %s WND',
+ helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),
+ );
+ expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+ });
+
+
+ // ensure that asset has been delivered
+ await helper.wait.newBlocks(3);
+
+ // expext collection id will be with id 1
+ const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+ balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);
+
+ console.log(
+ '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',
+ helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
+ );
+ console.log(
+ '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',
+ helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),
+ );
+ // commission has not paid in USDT token
+ expect(free).to.be.equal(TRANSFER_AMOUNT);
+ // ... and parachain native token
+ expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;
+ });
+
+ itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {
+ const destination = {
+ V1: {
+ parents: 1,
+ interior: {X2: [
+ {
+ Parachain: STATEMINE_CHAIN,
+ },
+ {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ },
+ ]},
+ },
+ };
+
+ const relayFee = 400_000_000_000_000n;
+ const currencies: [any, bigint][] = [
+ [
+ {
+ ForeignAssetId: 0,
+ },
+ TRANSFER_AMOUNT,
+ ],
+ [
+ {
+ NativeAssetId: 'Parent',
+ },
+ relayFee,
+ ],
+ ];
+
+ const feeItem = 1;
+
+ await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+
+ // the commission has been paid in parachain native token
+ balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);
+ console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzFinal - balanceQuartzAfter));
+ expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;
+
+ await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+ await helper.wait.newBlocks(3);
+
+ // The USDT token never paid fees. Its amount not changed from begin value.
+ // Also check that xcm transfer has been succeeded
+ expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
+ });
+ });
+
+ itSub('Should connect and send Relay token to Quartz', async ({helper}) => {
+ balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+ balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ const destination = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
+ },
+ }};
+
+ const beneficiary = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ AccountId32: {
+ network: 'Any',
+ id: bob.addressRaw,
+ },
+ }},
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: TRANSFER_AMOUNT_RELAY,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+ });
+
+ await helper.wait.newBlocks(3);
+
+ balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+ balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+ const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+ const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
+ console.log(
+ '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',
+ helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+ );
+ console.log(
+ '[Relay (Westend) -> Quartz] transaction fees: %s WND',
+ helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),
+ );
+ console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);
+ expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;
+ expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;
+ });
+
+ itSub('Should connect and send Relay token back', async ({helper}) => {
+ let relayTokenBalanceBefore: bigint;
+ let relayTokenBalanceAfter: bigint;
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+ });
+
+ const destination = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1:{
+ AccountId32: {
+ network: 'Any',
+ id: bob.addressRaw,
+ },
+ },
+ },
+ },
+ };
+
+ const currencies: any = [
+ [
+ {
+ NativeAssetId: 'Parent',
+ },
+ TRANSFER_AMOUNT_RELAY,
+ ],
+ ];
+
+ const feeItem = 0;
+
+ await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+ balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+ console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ await helper.wait.newBlocks(10);
+ relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+ const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+ console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+ expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+ });
+ });
+});
+
describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
let alice: IKeyringPair;
let randomAccount: IKeyringPair;
@@ -123,14 +532,13 @@
};
const feeAssetItem = 0;
- const weightLimit = 5000000000;
- await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);
+ await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
+ expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
- expect(qtzFees > 0n).to.be.true;
await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
await helper.wait.newBlocks(3);
@@ -173,9 +581,7 @@
ForeignAsset: 0,
};
- const destWeight = 50000000;
-
- await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);
+ await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');
balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
@@ -188,7 +594,7 @@
);
console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
- expect(karFees > 0).to.be.true;
+ expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;
expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
});
@@ -215,76 +621,7 @@
alice = await privateKey('//Alice');
});
});
-
- itSub('Quartz rejects tokens from the Relay', async ({helper}) => {
- await usingRelayPlaygrounds(relayUrl, async (helper) => {
- const destination = {
- V1: {
- parents: 0,
- interior: {X1: {
- Parachain: QUARTZ_CHAIN,
- },
- },
- }};
-
- const beneficiary = {
- V1: {
- parents: 0,
- interior: {X1: {
- AccountId32: {
- network: 'Any',
- id: alice.addressRaw,
- },
- }},
- },
- };
-
- const assets = {
- V1: [
- {
- id: {
- Concrete: {
- parents: 0,
- interior: 'Here',
- },
- },
- fun: {
- Fungible: 50_000_000_000_000_000n,
- },
- },
- ],
- };
-
- const feeAssetItem = 0;
- const weightLimit = 5_000_000_000;
-
- await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);
- });
- const maxWaitBlocks = 3;
-
- const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');
-
- expect(
- dmpQueueExecutedDownward != null,
- '[Relay] dmpQueue.ExecutedDownward event is expected',
- ).to.be.true;
-
- const event = dmpQueueExecutedDownward!.event;
- const outcome = event.data[1] as XcmV2TraitsOutcome;
-
- expect(
- outcome.isIncomplete,
- '[Relay] The outcome of the XCM should be `Incomplete`',
- ).to.be.true;
-
- const incomplete = outcome.asIncomplete;
- expect(
- incomplete[1].toString() == 'AssetNotFound',
- '[Relay] The XCM error should be `AssetNotFound`',
- ).to.be.true;
- });
-
itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
const destination = {
@@ -308,9 +645,7 @@
Token: 'KAR',
};
- const destWeight = 50000000;
-
- await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);
+ await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');
});
const maxWaitBlocks = 3;
@@ -326,8 +661,8 @@
const outcome = event.data[1] as XcmV2TraitsError;
expect(
- outcome.isUntrustedReserveLocation,
- '[Karura] The XCM error should be `UntrustedReserveLocation`',
+ outcome.isFailedToTransactAsset,
+ '[Karura] The XCM error should be `FailedToTransactAsset`',
).to.be.true;
});
});
@@ -420,7 +755,7 @@
// >>> Propose external motion through council >>>
console.log('Propose external motion through council.......');
- const externalMotion = helper.democracy.externalProposeMajority(proposalHash);
+ const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});
const encodedMotion = externalMotion?.method.toHex() || '';
const motionHash = blake2AsHex(encodedMotion);
console.log('Motion hash is %s', motionHash);
@@ -431,7 +766,16 @@
await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
- await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);
+ await helper.collective.council.close(
+ dorothyAccount,
+ motionHash,
+ councilProposalIdx,
+ {
+ refTime: 1_000_000_000,
+ proofSize: 1_000_000,
+ },
+ externalMotion.encodedLength,
+ );
console.log('Propose external motion through council.......DONE');
// <<< Propose external motion through council <<<
@@ -448,7 +792,16 @@
await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
- await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);
+ await helper.collective.techCommittee.close(
+ baltatharAccount,
+ fastTrackHash,
+ techProposalIdx,
+ {
+ refTime: 1_000_000_000,
+ proofSize: 1_000_000,
+ },
+ fastTrack.encodedLength,
+ );
console.log('Fast track proposal through technical committee.......DONE');
// <<< Fast track proposal through technical committee <<<
@@ -504,16 +857,15 @@
},
};
const amount = TRANSFER_AMOUNT;
- const destWeight = 850000000;
- await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);
+ await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');
balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);
expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;
const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));
- expect(transactionFees > 0).to.be.true;
+ expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
await helper.wait.newBlocks(3);
@@ -559,15 +911,14 @@
},
},
};
- const destWeight = 50000000;
- await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);
+ await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');
balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);
const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;
console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));
- expect(movrFees > 0).to.be.true;
+ expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;
const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);
tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -17,21 +17,430 @@
import {IKeyringPair} from '@polkadot/types/types';
import {blake2AsHex} from '@polkadot/util-crypto';
import config from '../config';
-import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util';
+import {XcmV2TraitsError} from '../interfaces';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds} from '../util';
const UNIQUE_CHAIN = 2037;
+const STATEMINT_CHAIN = 1000;
const ACALA_CHAIN = 2000;
const MOONBEAM_CHAIN = 2004;
+const STATEMINT_PALLET_INSTANCE = 50;
+
const relayUrl = config.relayUrl;
+const statemintUrl = config.statemintUrl;
const acalaUrl = config.acalaUrl;
const moonbeamUrl = config.moonbeamUrl;
+const RELAY_DECIMALS = 12;
+const STATEMINT_DECIMALS = 12;
const ACALA_DECIMALS = 12;
const TRANSFER_AMOUNT = 2000000000000000000000000n;
+const FUNDING_AMOUNT = 3_500_000_0000_000_000n;
+
+const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
+ let balanceStmnBefore: bigint;
+ let balanceStmnAfter: bigint;
+
+ let balanceUniqueBefore: bigint;
+ let balanceUniqueAfter: bigint;
+ let balanceUniqueFinal: bigint;
+
+ let balanceBobBefore: bigint;
+ let balanceBobAfter: bigint;
+ let balanceBobFinal: bigint;
+
+ let balanceBobRelayTokenBefore: bigint;
+ let balanceBobRelayTokenAfter: bigint;
+
+
+ before(async () => {
+ await usingPlaygrounds(async (_helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor
+ });
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ // Fund accounts on Statemint
+ await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+ await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
+ });
+
+ await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+ const sovereignFundingAmount = 3_500_000_000n;
+
+ await helper.assets.create(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ );
+ await helper.assets.setMetadata(
+ alice,
+ USDT_ASSET_ID,
+ USDT_ASSET_METADATA_NAME,
+ USDT_ASSET_METADATA_DESCRIPTION,
+ USDT_ASSET_METADATA_DECIMALS,
+ );
+ await helper.assets.mint(
+ alice,
+ USDT_ASSET_ID,
+ alice.address,
+ USDT_ASSET_AMOUNT,
+ );
+
+ // funding parachain sovereing account on Statemint.
+ // The sovereign account should be created before any action
+ // (the assets pallet on Statemint check if the sovereign account exists)
+ const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
+ await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
+ });
+
+
+ await usingPlaygrounds(async (helper) => {
+ const location = {
+ V1: {
+ parents: 1,
+ interior: {X3: [
+ {
+ Parachain: STATEMINT_CHAIN,
+ },
+ {
+ PalletInstance: STATEMINT_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
+ },
+ };
+
+ const metadata =
+ {
+ name: USDT_ASSET_ID,
+ symbol: USDT_ASSET_METADATA_NAME,
+ decimals: USDT_ASSET_METADATA_DECIMALS,
+ minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
+ };
+ await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+ balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);
+ });
+
+
+ // Providing the relay currency to the unique sender account
+ // (fee for USDT XCM are paid in relay tokens)
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ const destination = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ }};
+
+ const beneficiary = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ }},
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: TRANSFER_AMOUNT_RELAY,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+ });
+
+ });
+
+ itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {
+ await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+ const dest = {
+ V1: {
+ parents: 1,
+ interior: {X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ }};
+
+ const beneficiary = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ }},
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: {
+ X2: [
+ {
+ PalletInstance: STATEMINT_PALLET_INSTANCE,
+ },
+ {
+ GeneralIndex: USDT_ASSET_ID,
+ },
+ ]},
+ },
+ },
+ fun: {
+ Fungible: TRANSFER_AMOUNT,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+ await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+ balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+ // common good parachain take commission in it native token
+ console.log(
+ '[Statemint -> Unique] transaction fees on Statemint: %s WND',
+ helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),
+ );
+ expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+ });
+
+
+ // ensure that asset has been delivered
+ await helper.wait.newBlocks(3);
+
+ // expext collection id will be with id 1
+ const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+ balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);
+
+ console.log(
+ '[Statemint -> Unique] transaction fees on Unique: %s USDT',
+ helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
+ );
+ console.log(
+ '[Statemint -> Unique] transaction fees on Unique: %s UNQ',
+ helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),
+ );
+ // commission has not paid in USDT token
+ expect(free).to.be.equal(TRANSFER_AMOUNT);
+ // ... and parachain native token
+ expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;
+ });
+
+ itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {
+ const destination = {
+ V1: {
+ parents: 1,
+ interior: {X2: [
+ {
+ Parachain: STATEMINT_CHAIN,
+ },
+ {
+ AccountId32: {
+ network: 'Any',
+ id: alice.addressRaw,
+ },
+ },
+ ]},
+ },
+ };
+
+ const relayFee = 400_000_000_000_000n;
+ const currencies: [any, bigint][] = [
+ [
+ {
+ ForeignAssetId: 0,
+ },
+ TRANSFER_AMOUNT,
+ ],
+ [
+ {
+ NativeAssetId: 'Parent',
+ },
+ relayFee,
+ ],
+ ];
+
+ const feeItem = 1;
+
+ await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+
+ // the commission has been paid in parachain native token
+ balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);
+ console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueFinal - balanceUniqueAfter));
+ expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;
+
+ await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+ await helper.wait.newBlocks(3);
+
+ // The USDT token never paid fees. Its amount not changed from begin value.
+ // Also check that xcm transfer has been succeeded
+ expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
+ });
+ });
+
+ itSub('Should connect and send Relay token to Unique', async ({helper}) => {
+ balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+ balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ const destination = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ }};
+
+ const beneficiary = {
+ V1: {
+ parents: 0,
+ interior: {X1: {
+ AccountId32: {
+ network: 'Any',
+ id: bob.addressRaw,
+ },
+ }},
+ },
+ };
+
+ const assets = {
+ V1: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: TRANSFER_AMOUNT_RELAY,
+ },
+ },
+ ],
+ };
+
+ const feeAssetItem = 0;
+
+ await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+ });
+
+ await helper.wait.newBlocks(3);
+
+ balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+ balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+ const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+ const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
+ console.log(
+ '[Relay (Westend) -> Unique] transaction fees: %s UNQ',
+ helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+ );
+ console.log(
+ '[Relay (Westend) -> Unique] transaction fees: %s WND',
+ helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),
+ );
+ console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);
+ expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;
+ expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;
+ });
+
+ itSub('Should connect and send Relay token back', async ({helper}) => {
+ let relayTokenBalanceBefore: bigint;
+ let relayTokenBalanceAfter: bigint;
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+ });
+
+ const destination = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1:{
+ AccountId32: {
+ network: 'Any',
+ id: bob.addressRaw,
+ },
+ },
+ },
+ },
+ };
+
+ const currencies: any = [
+ [
+ {
+ NativeAssetId: 'Parent',
+ },
+ TRANSFER_AMOUNT_RELAY,
+ ],
+ ];
+
+ const feeItem = 0;
+
+ await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+ balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+ console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+ await usingRelayPlaygrounds(relayUrl, async (helper) => {
+ await helper.wait.newBlocks(10);
+ relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+ const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+ console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+ expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+ });
+ });
+});
+
describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
let alice: IKeyringPair;
let randomAccount: IKeyringPair;
@@ -124,15 +533,13 @@
};
const feeAssetItem = 0;
- const weightLimit = 5000000000;
- await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);
-
+ await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
- expect(unqFees > 0n).to.be.true;
+ expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
await helper.wait.newBlocks(3);
@@ -176,10 +583,7 @@
ForeignAsset: 0,
};
- const destWeight = 50000000;
-
- await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);
-
+ await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');
balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
@@ -192,7 +596,7 @@
);
console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
- expect(acaFees > 0).to.be.true;
+ expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;
expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
});
@@ -219,76 +623,7 @@
alice = await privateKey('//Alice');
});
});
-
- itSub('Unique rejects tokens from the Relay', async ({helper}) => {
- await usingRelayPlaygrounds(relayUrl, async (helper) => {
- const destination = {
- V1: {
- parents: 0,
- interior: {X1: {
- Parachain: UNIQUE_CHAIN,
- },
- },
- }};
- const beneficiary = {
- V1: {
- parents: 0,
- interior: {X1: {
- AccountId32: {
- network: 'Any',
- id: alice.addressRaw,
- },
- }},
- },
- };
-
- const assets = {
- V1: [
- {
- id: {
- Concrete: {
- parents: 0,
- interior: 'Here',
- },
- },
- fun: {
- Fungible: 50_000_000_000_000_000n,
- },
- },
- ],
- };
-
- const feeAssetItem = 0;
- const weightLimit = 5_000_000_000;
-
- await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);
- });
-
- const maxWaitBlocks = 3;
-
- const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');
-
- expect(
- dmpQueueExecutedDownward != null,
- '[Relay] dmpQueue.ExecutedDownward event is expected',
- ).to.be.true;
-
- const event = dmpQueueExecutedDownward!.event;
- const outcome = event.data[1] as XcmV2TraitsOutcome;
-
- expect(
- outcome.isIncomplete,
- '[Relay] The outcome of the XCM should be `Incomplete`',
- ).to.be.true;
-
- const incomplete = outcome.asIncomplete;
- expect(
- incomplete[1].toString() == 'AssetNotFound',
- '[Relay] The XCM error should be `AssetNotFound`',
- ).to.be.true;
- });
-
itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {
await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
const destination = {
@@ -312,9 +647,7 @@
Token: 'ACA',
};
- const destWeight = 50000000;
-
- await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);
+ await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');
});
const maxWaitBlocks = 3;
@@ -330,8 +663,8 @@
const outcome = event.data[1] as XcmV2TraitsError;
expect(
- outcome.isUntrustedReserveLocation,
- '[Acala] The XCM error should be `UntrustedReserveLocation`',
+ outcome.isFailedToTransactAsset,
+ '[Acala] The XCM error should be `FailedToTransactAsset`',
).to.be.true;
});
});
@@ -508,16 +841,15 @@
},
};
const amount = TRANSFER_AMOUNT;
- const destWeight = 850000000;
- await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);
+ await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');
balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);
expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;
const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));
- expect(transactionFees > 0).to.be.true;
+ expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
await helper.wait.newBlocks(3);
@@ -572,7 +904,7 @@
const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;
console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));
- expect(glmrFees > 0).to.be.true;
+ expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;
const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);