difftreelog
Merge pull request #801 from UniqueNetwork/fix/xcm-qtz-unq-tests
in: master
Fix xcm
10 files changed
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth161161162 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {162 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163 log::trace!(target: "fassets::get_currency_id", "call");163 log::trace!(target: "fassets::get_currency_id", "call");164 Some(AssetIds::ForeignAssetId(165 Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),164 Pallet::<T>::location_to_currency_ids(multi_location).map(|id| AssetIds::ForeignAssetId(id))166 ))167 }165 }168}166}169167runtime/common/config/xcm/foreignassets.rsdiffbeforeafterboth18 traits::{Contains, Get, fungibles},18 traits::{Contains, Get, fungibles},19 parameter_types,19 parameter_types,20};20};21use sp_runtime::traits::{Zero, Convert};21use sp_runtime::traits::Convert;22use xcm::v1::{Junction::*, MultiLocation, Junctions::*};22use xcm::v1::{Junction::*, MultiLocation, Junctions::*};23use xcm::latest::MultiAsset;23use xcm::latest::MultiAsset;24use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId};24use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId};38 pub CheckingAccount: AccountId = PolkadotXcm::check_account();38 pub CheckingAccount: AccountId = PolkadotXcm::check_account();39}39}404041/// Allow checking in assets that have issuance > 0.41/// No teleports are allowed42pub struct NonZeroIssuance<AccountId, ForeignAssets>(PhantomData<(AccountId, ForeignAssets)>);42pub struct NoTeleports<AccountId, ForeignAssets>(PhantomData<(AccountId, ForeignAssets)>);434344impl<AccountId, ForeignAssets> Contains<<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId>44impl<AccountId, ForeignAssets> Contains<<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId>45 for NonZeroIssuance<AccountId, ForeignAssets>45 for NoTeleports<AccountId, ForeignAssets>46where46where47 ForeignAssets: fungibles::Inspect<AccountId>,47 ForeignAssets: fungibles::Inspect<AccountId>,48{48{49 fn contains(id: &<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId) -> bool {49 fn contains(_id: &<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId) -> bool {50 !ForeignAssets::total_issuance(*id).is_zero()50 false51 }51 }52}52}535384 Some(AssetIds::ForeignAssetId(foreign_asset_id)) => {84 Some(AssetIds::ForeignAssetId(foreign_asset_id)) => {85 ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(foreign_asset_id))85 ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(foreign_asset_id))86 }86 }87 _ => ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(0)),87 _ => Err(()),88 }88 }89 }89 }9090132 LocationToAccountId,132 LocationToAccountId,133 // Our chain's account ID type (we can't get away without mentioning it explicitly):133 // Our chain's account ID type (we can't get away without mentioning it explicitly):134 AccountId,134 AccountId,135 // We only want to allow teleports of known assets. We use non-zero issuance as an indication135 // No teleports are allowed136 // that this asset is known.137 NonZeroIssuance<AccountId, ForeignAssets>,136 NoTeleports<AccountId, ForeignAssets>,138 // The account to use for tracking teleports.137 // The account to use for tracking teleports.139 CheckingAccount,138 CheckingAccount,140>;139>;runtime/common/config/xcm/mod.rsdiffbeforeafterboth186 TransferReserveAsset { dest: dst, .. } => {186 TransferReserveAsset { dest: dst, .. } => {187 allowed |= allowed_locations.contains(dst);187 allowed |= allowed_locations.contains(dst);188 }188 }189 InitiateReserveWithdraw { reserve: dst, .. } => {190 allowed |= allowed_locations.contains(dst);191 }189 _ => {}192 _ => {}190 });193 });191194tests/src/config.tsdiffbeforeafterboth25 moonbeamUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',25 moonbeamUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',26 moonriverUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',26 moonriverUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',27 westmintUrl: process.env.westmintUrl || 'ws://127.0.0.1:9948',27 westmintUrl: process.env.westmintUrl || 'ws://127.0.0.1:9948',28 statemineUrl: process.env.statemineUrl || 'ws://127.0.0.1:9948',29 statemintUrl: process.env.statemintUrl || 'ws://127.0.0.1:9948',28};30};293130export default config;32export default config;tests/src/util/index.tsdiffbeforeafterboth11import config from '../config';11import config from '../config';12import {ChainHelperBase} from './playgrounds/unique';12import {ChainHelperBase} from './playgrounds/unique';13import {ILogger} from './playgrounds/types';13import {ILogger} from './playgrounds/types';14import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';14import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper} from './playgrounds/unique.dev';151516chai.use(chaiAsPromised);16chai.use(chaiAsPromised);17chai.use(chaiSubset);17chai.use(chaiSubset);65 return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);65 return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);66};66};6768export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {69 return usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);70};7172export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {73 return usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);74};677568export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {76export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {69 return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);77 return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth119}119}120120121export class DevRelayHelper extends RelayHelper {}121export class DevRelayHelper extends RelayHelper {122 wait: WaitGroup;123124 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {125 options.helperBase = options.helperBase ?? DevRelayHelper;126127 super(logger, options);128 this.wait = new WaitGroup(this);129 }130}122131123export class DevWestmintHelper extends WestmintHelper {132export class DevWestmintHelper extends WestmintHelper {131 }140 }132}141}142143export class DevStatemineHelper extends DevWestmintHelper {}144145export class DevStatemintHelper extends DevWestmintHelper {}133146134export class DevMoonbeamHelper extends MoonbeamHelper {147export class DevMoonbeamHelper extends MoonbeamHelper {135 account: MoonbeamAccountGroup;148 account: MoonbeamAccountGroup;136 wait: WaitGroup;149 wait: WaitGroup;137150138 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {151 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {139 options.helperBase = options.helperBase ?? DevMoonbeamHelper;152 options.helperBase = options.helperBase ?? DevMoonbeamHelper;153 options.notePreimagePallet = options.notePreimagePallet ?? 'democracy';140154141 super(logger, options);155 super(logger, options);142 this.account = new MoonbeamAccountGroup(this);156 this.account = new MoonbeamAccountGroup(this);145}159}146160147export class DevMoonriverHelper extends DevMoonbeamHelper {}161export class DevMoonriverHelper extends DevMoonbeamHelper {162 constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {163 options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';164 super(logger, options);165 }166}148167149export class DevAcalaHelper extends AcalaHelper {168export class DevAcalaHelper extends AcalaHelper {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.tsdiffbeforeafterboth31const ASSET_METADATA_DESCRIPTION = 'USDT';31const ASSET_METADATA_DESCRIPTION = 'USDT';32const ASSET_METADATA_MINIMAL_BALANCE = 1n;32const ASSET_METADATA_MINIMAL_BALANCE = 1n;333334const RELAY_DECIMALS = 12;34const WESTMINT_DECIMALS = 12;35const WESTMINT_DECIMALS = 12;353636const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;37const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;147 };148 };148149149 const feeAssetItem = 0;150 const feeAssetItem = 0;150 const weightLimit = 5_000_000_000;151151152 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);152 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');153 });153 });154 154 155 });155 });202 };202 };203203204 const feeAssetItem = 0;204 const feeAssetItem = 0;205 const weightLimit = 5000000000;206205207 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);206 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);208 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, weightLimit);207 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');209208210 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);209 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);211210212 // common good parachain take commission in it native token211 // common good parachain take commission in it native token213 console.log(212 console.log(214 'Opal to Westmint transaction fees on Westmint: %s WND',213 '[Westmint -> Opal] transaction fees on Westmint: %s WND',215 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),214 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),216 );215 );217 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;216 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;227226228 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);227 balanceOpalAfter = await helper.balance.getSubstrate(alice.address);229228230 // commission has not paid in USDT token229 console.log(230 '[Westmint -> Opal] transaction fees on Opal: %s USDT',231 expect(free == TRANSFER_AMOUNT).to.be.true;231 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),232 );232 console.log(233 console.log(233 'Opal to Westmint transaction fees on Opal: %s USDT',234 '[Westmint -> Opal] transaction fees on Opal: %s OPL',234 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free),235 helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore),235 );236 );236 // ... and parachain native token237238 // commission has not paid in USDT token237 expect(balanceOpalAfter == balanceOpalBefore).to.be.true;239 expect(free == TRANSFER_AMOUNT).to.be.true;238 console.log(240 // ... and parachain native token239 'Opal to Westmint transaction fees on Opal: %s WND',240 helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),241 expect(balanceOpalAfter == balanceOpalBefore).to.be.true;241 ); 242 });242 });243243244 itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {244 itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {276 ];276 ];277277278 const feeItem = 1;278 const feeItem = 1;279 const destWeight = 500000000000;280279281 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, destWeight);280 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');282 281 283 // the commission has been paid in parachain native token282 // the commission has been paid in parachain native token284 balanceOpalFinal = await helper.balance.getSubstrate(alice.address);283 balanceOpalFinal = await helper.balance.getSubstrate(alice.address);339 };338 };340339341 const feeAssetItem = 0;340 const feeAssetItem = 0;342 const weightLimit = 5_000_000_000;343341344 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, weightLimit);342 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');345 });343 });346 344 347 await helper.wait.newBlocks(3);345 await helper.wait.newBlocks(3);363 });361 });364362365 itSub('Should connect and send Relay token back', async ({helper}) => {363 itSub('Should connect and send Relay token back', async ({helper}) => {364 let relayTokenBalanceBefore: bigint;365 let relayTokenBalanceAfter: bigint;366 await usingRelayPlaygrounds(relayUrl, async (helper) => {367 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);368 });369366 const destination = {370 const destination = {367 V1: {371 V1: {368 parents: 1,372 parents: 1,369 interior: {X2: [373 interior: {370 {371 Parachain: STATEMINE_CHAIN,372 },373 {374 X1:{374 AccountId32: {375 AccountId32: {375 network: 'Any',376 network: 'Any',376 id: bob.addressRaw,377 id: bob.addressRaw,377 },378 },378 },379 },379 ]},380 },380 },381 },381 };382 };382383390 ];391 ];391392392 const feeItem = 0;393 const feeItem = 0;393 const destWeight = 500000000000;394394395 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, destWeight);395 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');396396397 balanceBobFinal = await helper.balance.getSubstrate(bob.address);397 balanceBobFinal = await helper.balance.getSubstrate(bob.address);398 console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobFinal);398 console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));399400 await usingRelayPlaygrounds(relayUrl, async (helper) => {401 await helper.wait.newBlocks(10);402 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);403404 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;405 console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));406 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;407 });399 });408 });400});409});401410tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import {blake2AsHex} from '@polkadot/util-crypto';18import {blake2AsHex} from '@polkadot/util-crypto';19import config from '../config';19import config from '../config';20import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';20import {XcmV2TraitsError} from '../interfaces';21import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util';21import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds} from '../util';222223const QUARTZ_CHAIN = 2095;23const QUARTZ_CHAIN = 2095;24const STATEMINE_CHAIN = 1000;24const KARURA_CHAIN = 2000;25const KARURA_CHAIN = 2000;25const MOONRIVER_CHAIN = 2023;26const MOONRIVER_CHAIN = 2023;262728const STATEMINE_PALLET_INSTANCE = 50;2927const relayUrl = config.relayUrl;30const relayUrl = config.relayUrl;31const statemineUrl = config.statemineUrl;28const karuraUrl = config.karuraUrl;32const karuraUrl = config.karuraUrl;29const moonriverUrl = config.moonriverUrl;33const moonriverUrl = config.moonriverUrl;303435const RELAY_DECIMALS = 12;36const STATEMINE_DECIMALS = 12;31const KARURA_DECIMALS = 12;37const KARURA_DECIMALS = 12;323833const TRANSFER_AMOUNT = 2000000000000000000000000n;39const TRANSFER_AMOUNT = 2000000000000000000000000n;344041const FUNDING_AMOUNT = 3_500_000_0000_000_000n; 4243const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4445const USDT_ASSET_ID = 100;46const USDT_ASSET_METADATA_DECIMALS = 18;47const USDT_ASSET_METADATA_NAME = 'USDT';48const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';49const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;50const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5152describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {53 let alice: IKeyringPair;54 let bob: IKeyringPair;55 56 let balanceStmnBefore: bigint;57 let balanceStmnAfter: bigint;5859 let balanceQuartzBefore: bigint;60 let balanceQuartzAfter: bigint;61 let balanceQuartzFinal: bigint;6263 let balanceBobBefore: bigint;64 let balanceBobAfter: bigint;65 let balanceBobFinal: bigint;6667 let balanceBobRelayTokenBefore: bigint;68 let balanceBobRelayTokenAfter: bigint;697071 before(async () => {72 await usingPlaygrounds(async (_helper, privateKey) => {73 alice = await privateKey('//Alice');74 bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor75 });7677 await usingRelayPlaygrounds(relayUrl, async (helper) => {78 // Fund accounts on Statemine(t)79 await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);80 await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);81 });8283 await usingStateminePlaygrounds(statemineUrl, async (helper) => {84 const sovereignFundingAmount = 3_500_000_000n; 8586 await helper.assets.create(87 alice,88 USDT_ASSET_ID,89 alice.address,90 USDT_ASSET_METADATA_MINIMAL_BALANCE,91 );92 await helper.assets.setMetadata(93 alice,94 USDT_ASSET_ID,95 USDT_ASSET_METADATA_NAME,96 USDT_ASSET_METADATA_DESCRIPTION,97 USDT_ASSET_METADATA_DECIMALS,98 );99 await helper.assets.mint(100 alice,101 USDT_ASSET_ID,102 alice.address,103 USDT_ASSET_AMOUNT,104 );105106 // funding parachain sovereing account on Statemine(t).107 // The sovereign account should be created before any action108 // (the assets pallet on Statemine(t) check if the sovereign account exists)109 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);110 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);111 });112113114 await usingPlaygrounds(async (helper) => {115 const location = {116 V1: {117 parents: 1,118 interior: {X3: [119 {120 Parachain: STATEMINE_CHAIN,121 },122 {123 PalletInstance: STATEMINE_PALLET_INSTANCE,124 },125 {126 GeneralIndex: USDT_ASSET_ID,127 },128 ]},129 },130 };131132 const metadata =133 {134 name: USDT_ASSET_ID,135 symbol: USDT_ASSET_METADATA_NAME,136 decimals: USDT_ASSET_METADATA_DECIMALS,137 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,138 };139 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);140 balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);141 });142143144 // Providing the relay currency to the quartz sender account145 // (fee for USDT XCM are paid in relay tokens)146 await usingRelayPlaygrounds(relayUrl, async (helper) => {147 const destination = {148 V1: {149 parents: 0,150 interior: {X1: {151 Parachain: QUARTZ_CHAIN,152 },153 },154 }};155156 const beneficiary = {157 V1: {158 parents: 0,159 interior: {X1: {160 AccountId32: {161 network: 'Any',162 id: alice.addressRaw,163 },164 }},165 },166 };167168 const assets = {169 V1: [170 {171 id: {172 Concrete: {173 parents: 0,174 interior: 'Here',175 },176 },177 fun: {178 Fungible: TRANSFER_AMOUNT_RELAY,179 },180 },181 ],182 };183184 const feeAssetItem = 0;185186 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');187 });188 189 });190191 itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {192 await usingStateminePlaygrounds(statemineUrl, async (helper) => {193 const dest = {194 V1: {195 parents: 1,196 interior: {X1: {197 Parachain: QUARTZ_CHAIN,198 },199 },200 }};201202 const beneficiary = {203 V1: {204 parents: 0,205 interior: {X1: {206 AccountId32: {207 network: 'Any',208 id: alice.addressRaw,209 },210 }},211 },212 };213214 const assets = {215 V1: [216 {217 id: {218 Concrete: {219 parents: 0,220 interior: {221 X2: [222 {223 PalletInstance: STATEMINE_PALLET_INSTANCE,224 },225 {226 GeneralIndex: USDT_ASSET_ID,227 }, 228 ]},229 },230 },231 fun: {232 Fungible: TRANSFER_AMOUNT,233 },234 },235 ],236 };237238 const feeAssetItem = 0;239240 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);241 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');242243 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);244245 // common good parachain take commission in it native token246 console.log(247 '[Statemine -> Quartz] transaction fees on Statemine: %s WND',248 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),249 );250 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;251252 });253254255 // ensure that asset has been delivered256 await helper.wait.newBlocks(3);257258 // expext collection id will be with id 1259 const free = await helper.ft.getBalance(1, {Substrate: alice.address});260261 balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);262263 console.log(264 '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',265 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),266 );267 console.log(268 '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',269 helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),270 ); 271 // commission has not paid in USDT token272 expect(free).to.be.equal(TRANSFER_AMOUNT);273 // ... and parachain native token274 expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;275 });276277 itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {278 const destination = {279 V1: {280 parents: 1,281 interior: {X2: [282 {283 Parachain: STATEMINE_CHAIN,284 },285 {286 AccountId32: {287 network: 'Any',288 id: alice.addressRaw,289 },290 },291 ]},292 },293 };294295 const relayFee = 400_000_000_000_000n;296 const currencies: [any, bigint][] = [297 [298 {299 ForeignAssetId: 0,300 },301 TRANSFER_AMOUNT,302 ], 303 [304 {305 NativeAssetId: 'Parent',306 },307 relayFee,308 ],309 ];310311 const feeItem = 1;312313 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');314 315 // the commission has been paid in parachain native token316 balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);317 console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzFinal - balanceQuartzAfter));318 expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;319320 await usingStateminePlaygrounds(statemineUrl, async (helper) => {321 await helper.wait.newBlocks(3);322 323 // The USDT token never paid fees. Its amount not changed from begin value.324 // Also check that xcm transfer has been succeeded 325 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;326 });327 });328329 itSub('Should connect and send Relay token to Quartz', async ({helper}) => {330 balanceBobBefore = await helper.balance.getSubstrate(bob.address);331 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});332333 await usingRelayPlaygrounds(relayUrl, async (helper) => {334 const destination = {335 V1: {336 parents: 0,337 interior: {X1: {338 Parachain: QUARTZ_CHAIN,339 },340 },341 }};342343 const beneficiary = {344 V1: {345 parents: 0,346 interior: {X1: {347 AccountId32: {348 network: 'Any',349 id: bob.addressRaw,350 },351 }},352 },353 };354355 const assets = {356 V1: [357 {358 id: {359 Concrete: {360 parents: 0,361 interior: 'Here',362 },363 },364 fun: {365 Fungible: TRANSFER_AMOUNT_RELAY,366 },367 },368 ],369 };370371 const feeAssetItem = 0;372373 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');374 });375 376 await helper.wait.newBlocks(3);377378 balanceBobAfter = await helper.balance.getSubstrate(bob.address); 379 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});380381 const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;382 const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;383 console.log(384 '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',385 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),386 );387 console.log(388 '[Relay (Westend) -> Quartz] transaction fees: %s WND',389 helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),390 );391 console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);392 expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;393 expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;394 });395396 itSub('Should connect and send Relay token back', async ({helper}) => {397 let relayTokenBalanceBefore: bigint;398 let relayTokenBalanceAfter: bigint;399 await usingRelayPlaygrounds(relayUrl, async (helper) => {400 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);401 });402403 const destination = {404 V1: {405 parents: 1,406 interior: {407 X1:{408 AccountId32: {409 network: 'Any',410 id: bob.addressRaw,411 },412 },413 },414 },415 };416417 const currencies: any = [418 [419 {420 NativeAssetId: 'Parent',421 },422 TRANSFER_AMOUNT_RELAY,423 ],424 ];425426 const feeItem = 0;427428 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');429430 balanceBobFinal = await helper.balance.getSubstrate(bob.address);431 console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));432433 await usingRelayPlaygrounds(relayUrl, async (helper) => {434 await helper.wait.newBlocks(10);435 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);436437 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;438 console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));439 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;440 });441 });442});44335describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {444describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {36 let alice: IKeyringPair;445 let alice: IKeyringPair;123 };532 };124533125 const feeAssetItem = 0;534 const feeAssetItem = 0;126 const weightLimit = 5000000000;127535128 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);536 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');129 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);537 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);130538131 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;539 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;540 expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;132 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));541 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));133 expect(qtzFees > 0n).to.be.true;134542135 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {543 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {136 await helper.wait.newBlocks(3);544 await helper.wait.newBlocks(3);173 ForeignAsset: 0,581 ForeignAsset: 0,174 };582 };175583176 const destWeight = 50000000;584 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');177178 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);179 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);585 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);180 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);586 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);181587188 );594 );189 console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));595 console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));190596191 expect(karFees > 0).to.be.true;597 expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;192 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;598 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;193 });599 });194600216 });622 });217 });623 });218624219 itSub('Quartz rejects tokens from the Relay', async ({helper}) => {220 await usingRelayPlaygrounds(relayUrl, async (helper) => {221 const destination = {222 V1: {223 parents: 0,224 interior: {X1: {225 Parachain: QUARTZ_CHAIN,226 },227 },228 }};229230 const beneficiary = {231 V1: {232 parents: 0,233 interior: {X1: {234 AccountId32: {235 network: 'Any',236 id: alice.addressRaw,237 },238 }},239 },240 };241242 const assets = {243 V1: [244 {245 id: {246 Concrete: {247 parents: 0,248 interior: 'Here',249 },250 },251 fun: {252 Fungible: 50_000_000_000_000_000n,253 },254 },255 ],256 };257258 const feeAssetItem = 0;259 const weightLimit = 5_000_000_000;260261 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);262 });263264 const maxWaitBlocks = 3;265266 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');267268 expect(269 dmpQueueExecutedDownward != null,270 '[Relay] dmpQueue.ExecutedDownward event is expected',271 ).to.be.true;272273 const event = dmpQueueExecutedDownward!.event;274 const outcome = event.data[1] as XcmV2TraitsOutcome;275276 expect(277 outcome.isIncomplete,278 '[Relay] The outcome of the XCM should be `Incomplete`',279 ).to.be.true;280281 const incomplete = outcome.asIncomplete;282 expect(283 incomplete[1].toString() == 'AssetNotFound',284 '[Relay] The XCM error should be `AssetNotFound`',285 ).to.be.true;286 });287288 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {625 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {289 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {626 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {290 const destination = {627 const destination = {308 Token: 'KAR',645 Token: 'KAR',309 };646 };310647311 const destWeight = 50000000;648 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');312313 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);314 });649 });315650316 const maxWaitBlocks = 3;651 const maxWaitBlocks = 3;326 const outcome = event.data[1] as XcmV2TraitsError;661 const outcome = event.data[1] as XcmV2TraitsError;327662328 expect(663 expect(329 outcome.isUntrustedReserveLocation,664 outcome.isFailedToTransactAsset,330 '[Karura] The XCM error should be `UntrustedReserveLocation`',665 '[Karura] The XCM error should be `FailedToTransactAsset`',331 ).to.be.true;666 ).to.be.true;332 });667 });333});668});420755421 // >>> Propose external motion through council >>>756 // >>> Propose external motion through council >>>422 console.log('Propose external motion through council.......');757 console.log('Propose external motion through council.......');423 const externalMotion = helper.democracy.externalProposeMajority(proposalHash);758 const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});424 const encodedMotion = externalMotion?.method.toHex() || '';759 const encodedMotion = externalMotion?.method.toHex() || '';425 const motionHash = blake2AsHex(encodedMotion);760 const motionHash = blake2AsHex(encodedMotion);426 console.log('Motion hash is %s', motionHash);761 console.log('Motion hash is %s', motionHash);431 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);766 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);432 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);767 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);433768434 await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);769 await helper.collective.council.close(770 dorothyAccount,771 motionHash,772 councilProposalIdx,773 {774 refTime: 1_000_000_000,775 proofSize: 1_000_000,776 },777 externalMotion.encodedLength,778 );435 console.log('Propose external motion through council.......DONE');779 console.log('Propose external motion through council.......DONE');436 // <<< Propose external motion through council <<<780 // <<< Propose external motion through council <<<437781448 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);792 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);449 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);793 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);450794451 await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);795 await helper.collective.techCommittee.close(796 baltatharAccount,797 fastTrackHash,798 techProposalIdx,799 {800 refTime: 1_000_000_000,801 proofSize: 1_000_000,802 },803 fastTrack.encodedLength,804 );452 console.log('Fast track proposal through technical committee.......DONE');805 console.log('Fast track proposal through technical committee.......DONE');453 // <<< Fast track proposal through technical committee <<<806 // <<< Fast track proposal through technical committee <<<454807504 },857 },505 };858 };506 const amount = TRANSFER_AMOUNT;859 const amount = TRANSFER_AMOUNT;507 const destWeight = 850000000;508860509 await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);861 await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');510862511 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);863 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);512 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;864 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;513865514 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;866 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;515 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));867 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));516 expect(transactionFees > 0).to.be.true;868 expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;517869518 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {870 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {519 await helper.wait.newBlocks(3);871 await helper.wait.newBlocks(3);559 },911 },560 },912 },561 };913 };562 const destWeight = 50000000;563914564 await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);915 await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');565916566 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);917 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);567918568 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;919 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;569 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));920 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));570 expect(movrFees > 0).to.be.true;921 expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;571922572 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);923 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);573924tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import {blake2AsHex} from '@polkadot/util-crypto';18import {blake2AsHex} from '@polkadot/util-crypto';19import config from '../config';19import config from '../config';20import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';20import {XcmV2TraitsError} from '../interfaces';21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util';21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds} from '../util';222223const UNIQUE_CHAIN = 2037;23const UNIQUE_CHAIN = 2037;24const STATEMINT_CHAIN = 1000;24const ACALA_CHAIN = 2000;25const ACALA_CHAIN = 2000;25const MOONBEAM_CHAIN = 2004;26const MOONBEAM_CHAIN = 2004;262728const STATEMINT_PALLET_INSTANCE = 50;2927const relayUrl = config.relayUrl;30const relayUrl = config.relayUrl;31const statemintUrl = config.statemintUrl;28const acalaUrl = config.acalaUrl;32const acalaUrl = config.acalaUrl;29const moonbeamUrl = config.moonbeamUrl;33const moonbeamUrl = config.moonbeamUrl;303435const RELAY_DECIMALS = 12;36const STATEMINT_DECIMALS = 12;31const ACALA_DECIMALS = 12;37const ACALA_DECIMALS = 12;323833const TRANSFER_AMOUNT = 2000000000000000000000000n;39const TRANSFER_AMOUNT = 2000000000000000000000000n;344041const FUNDING_AMOUNT = 3_500_000_0000_000_000n; 4243const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4445const USDT_ASSET_ID = 100;46const USDT_ASSET_METADATA_DECIMALS = 18;47const USDT_ASSET_METADATA_NAME = 'USDT';48const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';49const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;50const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5152describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {53 let alice: IKeyringPair;54 let bob: IKeyringPair;55 56 let balanceStmnBefore: bigint;57 let balanceStmnAfter: bigint;5859 let balanceUniqueBefore: bigint;60 let balanceUniqueAfter: bigint;61 let balanceUniqueFinal: bigint;6263 let balanceBobBefore: bigint;64 let balanceBobAfter: bigint;65 let balanceBobFinal: bigint;6667 let balanceBobRelayTokenBefore: bigint;68 let balanceBobRelayTokenAfter: bigint;697071 before(async () => {72 await usingPlaygrounds(async (_helper, privateKey) => {73 alice = await privateKey('//Alice');74 bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor75 });7677 await usingRelayPlaygrounds(relayUrl, async (helper) => {78 // Fund accounts on Statemint79 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);80 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);81 });8283 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {84 const sovereignFundingAmount = 3_500_000_000n; 8586 await helper.assets.create(87 alice,88 USDT_ASSET_ID,89 alice.address,90 USDT_ASSET_METADATA_MINIMAL_BALANCE,91 );92 await helper.assets.setMetadata(93 alice,94 USDT_ASSET_ID,95 USDT_ASSET_METADATA_NAME,96 USDT_ASSET_METADATA_DESCRIPTION,97 USDT_ASSET_METADATA_DECIMALS,98 );99 await helper.assets.mint(100 alice,101 USDT_ASSET_ID,102 alice.address,103 USDT_ASSET_AMOUNT,104 );105106 // funding parachain sovereing account on Statemint.107 // The sovereign account should be created before any action108 // (the assets pallet on Statemint check if the sovereign account exists)109 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);110 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);111 });112113114 await usingPlaygrounds(async (helper) => {115 const location = {116 V1: {117 parents: 1,118 interior: {X3: [119 {120 Parachain: STATEMINT_CHAIN,121 },122 {123 PalletInstance: STATEMINT_PALLET_INSTANCE,124 },125 {126 GeneralIndex: USDT_ASSET_ID,127 },128 ]},129 },130 };131132 const metadata =133 {134 name: USDT_ASSET_ID,135 symbol: USDT_ASSET_METADATA_NAME,136 decimals: USDT_ASSET_METADATA_DECIMALS,137 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,138 };139 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);140 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);141 });142143144 // Providing the relay currency to the unique sender account145 // (fee for USDT XCM are paid in relay tokens)146 await usingRelayPlaygrounds(relayUrl, async (helper) => {147 const destination = {148 V1: {149 parents: 0,150 interior: {X1: {151 Parachain: UNIQUE_CHAIN,152 },153 },154 }};155156 const beneficiary = {157 V1: {158 parents: 0,159 interior: {X1: {160 AccountId32: {161 network: 'Any',162 id: alice.addressRaw,163 },164 }},165 },166 };167168 const assets = {169 V1: [170 {171 id: {172 Concrete: {173 parents: 0,174 interior: 'Here',175 },176 },177 fun: {178 Fungible: TRANSFER_AMOUNT_RELAY,179 },180 },181 ],182 };183184 const feeAssetItem = 0;185186 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');187 });188 189 });190191 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {192 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {193 const dest = {194 V1: {195 parents: 1,196 interior: {X1: {197 Parachain: UNIQUE_CHAIN,198 },199 },200 }};201202 const beneficiary = {203 V1: {204 parents: 0,205 interior: {X1: {206 AccountId32: {207 network: 'Any',208 id: alice.addressRaw,209 },210 }},211 },212 };213214 const assets = {215 V1: [216 {217 id: {218 Concrete: {219 parents: 0,220 interior: {221 X2: [222 {223 PalletInstance: STATEMINT_PALLET_INSTANCE,224 },225 {226 GeneralIndex: USDT_ASSET_ID,227 }, 228 ]},229 },230 },231 fun: {232 Fungible: TRANSFER_AMOUNT,233 },234 },235 ],236 };237238 const feeAssetItem = 0;239240 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);241 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');242243 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);244245 // common good parachain take commission in it native token246 console.log(247 '[Statemint -> Unique] transaction fees on Statemint: %s WND',248 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),249 );250 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;251252 });253254255 // ensure that asset has been delivered256 await helper.wait.newBlocks(3);257258 // expext collection id will be with id 1259 const free = await helper.ft.getBalance(1, {Substrate: alice.address});260261 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);262263 console.log(264 '[Statemint -> Unique] transaction fees on Unique: %s USDT',265 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),266 );267 console.log(268 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',269 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),270 ); 271 // commission has not paid in USDT token272 expect(free).to.be.equal(TRANSFER_AMOUNT);273 // ... and parachain native token274 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;275 });276277 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {278 const destination = {279 V1: {280 parents: 1,281 interior: {X2: [282 {283 Parachain: STATEMINT_CHAIN,284 },285 {286 AccountId32: {287 network: 'Any',288 id: alice.addressRaw,289 },290 },291 ]},292 },293 };294295 const relayFee = 400_000_000_000_000n;296 const currencies: [any, bigint][] = [297 [298 {299 ForeignAssetId: 0,300 },301 TRANSFER_AMOUNT,302 ], 303 [304 {305 NativeAssetId: 'Parent',306 },307 relayFee,308 ],309 ];310311 const feeItem = 1;312313 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');314 315 // the commission has been paid in parachain native token316 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);317 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueFinal - balanceUniqueAfter));318 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;319320 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {321 await helper.wait.newBlocks(3);322 323 // The USDT token never paid fees. Its amount not changed from begin value.324 // Also check that xcm transfer has been succeeded 325 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;326 });327 });328329 itSub('Should connect and send Relay token to Unique', async ({helper}) => {330 balanceBobBefore = await helper.balance.getSubstrate(bob.address);331 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});332333 await usingRelayPlaygrounds(relayUrl, async (helper) => {334 const destination = {335 V1: {336 parents: 0,337 interior: {X1: {338 Parachain: UNIQUE_CHAIN,339 },340 },341 }};342343 const beneficiary = {344 V1: {345 parents: 0,346 interior: {X1: {347 AccountId32: {348 network: 'Any',349 id: bob.addressRaw,350 },351 }},352 },353 };354355 const assets = {356 V1: [357 {358 id: {359 Concrete: {360 parents: 0,361 interior: 'Here',362 },363 },364 fun: {365 Fungible: TRANSFER_AMOUNT_RELAY,366 },367 },368 ],369 };370371 const feeAssetItem = 0;372373 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');374 });375 376 await helper.wait.newBlocks(3);377378 balanceBobAfter = await helper.balance.getSubstrate(bob.address); 379 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});380381 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;382 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;383 console.log(384 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',385 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),386 );387 console.log(388 '[Relay (Westend) -> Unique] transaction fees: %s WND',389 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),390 );391 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);392 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;393 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;394 });395396 itSub('Should connect and send Relay token back', async ({helper}) => {397 let relayTokenBalanceBefore: bigint;398 let relayTokenBalanceAfter: bigint;399 await usingRelayPlaygrounds(relayUrl, async (helper) => {400 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);401 });402403 const destination = {404 V1: {405 parents: 1,406 interior: {407 X1:{408 AccountId32: {409 network: 'Any',410 id: bob.addressRaw,411 },412 },413 },414 },415 };416417 const currencies: any = [418 [419 {420 NativeAssetId: 'Parent',421 },422 TRANSFER_AMOUNT_RELAY,423 ],424 ];425426 const feeItem = 0;427428 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');429430 balanceBobFinal = await helper.balance.getSubstrate(bob.address);431 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));432433 await usingRelayPlaygrounds(relayUrl, async (helper) => {434 await helper.wait.newBlocks(10);435 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);436437 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;438 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));439 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;440 });441 });442});44335describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {444describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {36 let alice: IKeyringPair;445 let alice: IKeyringPair;124 };533 };125534126 const feeAssetItem = 0;535 const feeAssetItem = 0;127 const weightLimit = 5000000000;128536129 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);537 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');130131 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);538 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);132539133 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;540 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;134 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));541 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));135 expect(unqFees > 0n).to.be.true;542 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;136543137 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {544 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {138 await helper.wait.newBlocks(3);545 await helper.wait.newBlocks(3);176 ForeignAsset: 0,583 ForeignAsset: 0,177 };584 };178585179 const destWeight = 50000000;586 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');180181 await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);182183 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);587 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);184 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);588 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);192 );596 );193 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));597 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));194598195 expect(acaFees > 0).to.be.true;599 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;196 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;600 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;197 });601 });198602220 });624 });221 });625 });222626223 itSub('Unique rejects tokens from the Relay', async ({helper}) => {224 await usingRelayPlaygrounds(relayUrl, async (helper) => {225 const destination = {226 V1: {227 parents: 0,228 interior: {X1: {229 Parachain: UNIQUE_CHAIN,230 },231 },232 }};233234 const beneficiary = {235 V1: {236 parents: 0,237 interior: {X1: {238 AccountId32: {239 network: 'Any',240 id: alice.addressRaw,241 },242 }},243 },244 };245246 const assets = {247 V1: [248 {249 id: {250 Concrete: {251 parents: 0,252 interior: 'Here',253 },254 },255 fun: {256 Fungible: 50_000_000_000_000_000n,257 },258 },259 ],260 };261262 const feeAssetItem = 0;263 const weightLimit = 5_000_000_000;264265 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);266 });267268 const maxWaitBlocks = 3;269270 const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');271272 expect(273 dmpQueueExecutedDownward != null,274 '[Relay] dmpQueue.ExecutedDownward event is expected',275 ).to.be.true;276277 const event = dmpQueueExecutedDownward!.event;278 const outcome = event.data[1] as XcmV2TraitsOutcome;279280 expect(281 outcome.isIncomplete,282 '[Relay] The outcome of the XCM should be `Incomplete`',283 ).to.be.true;284285 const incomplete = outcome.asIncomplete;286 expect(287 incomplete[1].toString() == 'AssetNotFound',288 '[Relay] The XCM error should be `AssetNotFound`',289 ).to.be.true;290 });291292 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {627 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {293 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {628 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {294 const destination = {629 const destination = {311 const id = {646 const id = {312 Token: 'ACA',647 Token: 'ACA',313 };648 };314315 const destWeight = 50000000;316649317 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);650 await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');318 });651 });319652320 const maxWaitBlocks = 3;653 const maxWaitBlocks = 3;330 const outcome = event.data[1] as XcmV2TraitsError;663 const outcome = event.data[1] as XcmV2TraitsError;331664332 expect(665 expect(333 outcome.isUntrustedReserveLocation,666 outcome.isFailedToTransactAsset,334 '[Acala] The XCM error should be `UntrustedReserveLocation`',667 '[Acala] The XCM error should be `FailedToTransactAsset`',335 ).to.be.true;668 ).to.be.true;336 });669 });337});670});508 },841 },509 };842 };510 const amount = TRANSFER_AMOUNT;843 const amount = TRANSFER_AMOUNT;511 const destWeight = 850000000;512844513 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);845 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');514846515 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);847 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);516 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;848 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;517849518 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;850 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;519 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));851 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));520 expect(transactionFees > 0).to.be.true;852 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;521853522 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {854 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {523 await helper.wait.newBlocks(3);855 await helper.wait.newBlocks(3);572904573 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;905 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;574 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));906 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));575 expect(glmrFees > 0).to.be.true;907 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;576908577 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);909 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);578910