difftreelog
test other chains can spend only up to balance
in: master
4 files changed
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -420,6 +420,63 @@
return capture;
}
+
+ makeXcmProgramWithdrawDeposit(beneficiary: Uint8Array, amount: bigint | string) {
+ return {
+ V2: [
+ {
+ WithdrawAsset: [
+ {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ],
+ },
+ {
+ BuyExecution: {
+ fees: {
+ id: {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ fun: {
+ Fungible: amount,
+ },
+ },
+ weightLimit: 'Unlimited'
+ },
+ },
+ {
+ DepositAsset: {
+ assets: {
+ Wild: 'All'
+ },
+ maxAssets: 1,
+ beneficiary: {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ network: 'Any',
+ id: beneficiary
+ },
+ },
+ },
+ },
+ }
+ },
+ ],
+ };
+ }
}
class MoonbeamAccountGroup {
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2446,6 +2446,10 @@
return this.ethBalanceGroup.getEthereum(address);
}
+ async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string, reservedAmount: bigint | string = 0n) {
+ await this.helper.executeExtrinsic(signer, 'api.tx.balances.setBalance', [address, amount, reservedAmount], true);
+ }
+
/**
* Transfer tokens to substrate address
* @param signer keyring of signer
tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -642,6 +642,84 @@
console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
expect(qtzFees == 0n).to.be.true;
});
+
+ itSub('Karura can send only up to its balance', async ({helper}) => {
+ // set Karura's sovereign account's balance
+ const karuraBalance = 10000n * (10n ** QTZ_DECIMALS);
+ const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN);
+ await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance);
+
+ const moreThanKaruraHas = karuraBalance * 2n;
+
+ let targetAccountBalance = 0n;
+ const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+ const quartzMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1: {Parachain: QUARTZ_CHAIN},
+ },
+ },
+ };
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(targetAccount.addressRaw, moreThanKaruraHas);
+
+ // Try to trick Quartz
+ await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+ await helper.getSudo().executeExtrinsic(
+ alice,
+ 'api.tx.polkadotXcm.send',
+ [
+ quartzMultilocation,
+ maliciousXcmProgram,
+ ],
+ true,
+ );
+ });
+
+ const maxWaitBlocks = 3;
+
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
+
+ expect(
+ xcmpQueueFailEvent != null,
+ `'xcmpQueue.FailEvent' event is expected`,
+ ).to.be.true;
+
+ expect(
+ xcmpQueueFailEvent!.isFailedToTransactAsset,
+ `The XCM error should be 'FailedToTransactAsset'`,
+ ).to.be.true;
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(0n);
+
+ // But Karura still can send the correct amount
+ const validTransferAmount = karuraBalance / 2n;
+ const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(targetAccount.addressRaw, validTransferAmount);
+
+ await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+ await helper.getSudo().executeExtrinsic(
+ alice,
+ 'api.tx.polkadotXcm.send',
+ [
+ quartzMultilocation,
+ validXcmProgram,
+ ],
+ true,
+ );
+ });
+
+ await helper.wait.newBlocks(maxWaitBlocks);
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(validTransferAmount);
+ });
});
// These tests are relevant only when
@@ -1060,6 +1138,10 @@
console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
expect(qtzFees == 0n).to.be.true;
});
+
+ itSub.skip('Moonriver can send only up to its balance', async ({helper}) => {
+ throw Error("Not yet implemented");
+ });
});
describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
@@ -1272,4 +1354,82 @@
console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`);
expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);
});
+
+ itSub('Shiden can send only up to its balance', async ({helper}) => {
+ // set Shiden's sovereign account's balance
+ const shidenBalance = 10000n * (10n ** QTZ_DECIMALS);
+ const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN);
+ await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance);
+
+ const moreThanShidenHas = shidenBalance * 2n;
+
+ let targetAccountBalance = 0n;
+ const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+ const quartzMultilocation = {
+ V1: {
+ parents: 1,
+ interior: {
+ X1: {Parachain: QUARTZ_CHAIN},
+ },
+ },
+ };
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(targetAccount.addressRaw, moreThanShidenHas);
+
+ // Try to trick Quartz
+ await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+ await helper.getSudo().executeExtrinsic(
+ alice,
+ 'api.tx.polkadotXcm.send',
+ [
+ quartzMultilocation,
+ maliciousXcmProgram,
+ ],
+ true,
+ );
+ });
+
+ const maxWaitBlocks = 3;
+
+ const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(
+ maxWaitBlocks,
+ 'xcmpQueue',
+ 'Fail',
+ );
+
+ expect(
+ xcmpQueueFailEvent != null,
+ `'xcmpQueue.FailEvent' event is expected`,
+ ).to.be.true;
+
+ expect(
+ xcmpQueueFailEvent!.isFailedToTransactAsset,
+ `The XCM error should be 'FailedToTransactAsset'`,
+ ).to.be.true;
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(0n);
+
+ // But Shiden still can send the correct amount
+ const validTransferAmount = shidenBalance / 2n;
+ const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(targetAccount.addressRaw, validTransferAmount);
+
+ await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+ await helper.getSudo().executeExtrinsic(
+ alice,
+ 'api.tx.polkadotXcm.send',
+ [
+ quartzMultilocation,
+ validXcmProgram,
+ ],
+ true,
+ );
+ });
+
+ await helper.wait.newBlocks(maxWaitBlocks);
+
+ targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(validTransferAmount);
+ });
});
tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {blake2AsHex} from '@polkadot/util-crypto';19import config from '../config';20import {XcmV2TraitsError} from '../interfaces';21import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';22import {DevUniqueHelper} from '../util/playgrounds/unique.dev';2324const UNIQUE_CHAIN = 2037;25const STATEMINT_CHAIN = 1000;26const ACALA_CHAIN = 2000;27const MOONBEAM_CHAIN = 2004;28const ASTAR_CHAIN = 2006;2930const STATEMINT_PALLET_INSTANCE = 50;3132const relayUrl = config.relayUrl;33const statemintUrl = config.statemintUrl;34const acalaUrl = config.acalaUrl;35const moonbeamUrl = config.moonbeamUrl;36const astarUrl = config.astarUrl;3738const RELAY_DECIMALS = 12;39const STATEMINT_DECIMALS = 12;40const ACALA_DECIMALS = 12;41const ASTAR_DECIMALS = 18n;42const UNQ_DECIMALS = 18n;4344const TRANSFER_AMOUNT = 2000000000000000000000000n;4546const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4748const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4950const USDT_ASSET_ID = 100;51const USDT_ASSET_METADATA_DECIMALS = 18;52const USDT_ASSET_METADATA_NAME = 'USDT';53const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';54const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;55const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5657const SAFE_XCM_VERSION = 2;5859describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {60 let alice: IKeyringPair;61 let bob: IKeyringPair;6263 let balanceStmnBefore: bigint;64 let balanceStmnAfter: bigint;6566 let balanceUniqueBefore: bigint;67 let balanceUniqueAfter: bigint;68 let balanceUniqueFinal: bigint;6970 let balanceBobBefore: bigint;71 let balanceBobAfter: bigint;72 let balanceBobFinal: bigint;7374 let balanceBobRelayTokenBefore: bigint;75 let balanceBobRelayTokenAfter: bigint;767778 before(async () => {79 await usingPlaygrounds(async (helper, privateKey) => {80 alice = await privateKey('//Alice');81 bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8283 // Set the default version to wrap the first message to other chains.84 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);85 });8687 await usingRelayPlaygrounds(relayUrl, async (helper) => {88 // Fund accounts on Statemint89 const relayXcmVersion = 2;90 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT, relayXcmVersion);91 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT, relayXcmVersion);92 });9394 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {95 const sovereignFundingAmount = 3_500_000_000n;9697 await helper.assets.create(98 alice,99 USDT_ASSET_ID,100 alice.address,101 USDT_ASSET_METADATA_MINIMAL_BALANCE,102 );103 await helper.assets.setMetadata(104 alice,105 USDT_ASSET_ID,106 USDT_ASSET_METADATA_NAME,107 USDT_ASSET_METADATA_DESCRIPTION,108 USDT_ASSET_METADATA_DECIMALS,109 );110 await helper.assets.mint(111 alice,112 USDT_ASSET_ID,113 alice.address,114 USDT_ASSET_AMOUNT,115 );116117 // funding parachain sovereing account on Statemint.118 // The sovereign account should be created before any action119 // (the assets pallet on Statemint check if the sovereign account exists)120 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);121 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);122 });123124125 await usingPlaygrounds(async (helper) => {126 const location = {127 V2: {128 parents: 1,129 interior: {X3: [130 {131 Parachain: STATEMINT_CHAIN,132 },133 {134 PalletInstance: STATEMINT_PALLET_INSTANCE,135 },136 {137 GeneralIndex: USDT_ASSET_ID,138 },139 ]},140 },141 };142143 const metadata =144 {145 name: USDT_ASSET_ID,146 symbol: USDT_ASSET_METADATA_NAME,147 decimals: USDT_ASSET_METADATA_DECIMALS,148 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,149 };150 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);151 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);152 });153154155 // Providing the relay currency to the unique sender account156 // (fee for USDT XCM are paid in relay tokens)157 await usingRelayPlaygrounds(relayUrl, async (helper) => {158 const destination = {159 V1: {160 parents: 0,161 interior: {X1: {162 Parachain: UNIQUE_CHAIN,163 },164 },165 }};166167 const beneficiary = {168 V1: {169 parents: 0,170 interior: {X1: {171 AccountId32: {172 network: 'Any',173 id: alice.addressRaw,174 },175 }},176 },177 };178179 const assets = {180 V1: [181 {182 id: {183 Concrete: {184 parents: 0,185 interior: 'Here',186 },187 },188 fun: {189 Fungible: TRANSFER_AMOUNT_RELAY,190 },191 },192 ],193 };194195 const feeAssetItem = 0;196197 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');198 });199200 });201202 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {203 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {204 const dest = {205 V1: {206 parents: 1,207 interior: {X1: {208 Parachain: UNIQUE_CHAIN,209 },210 },211 }};212213 const beneficiary = {214 V1: {215 parents: 0,216 interior: {X1: {217 AccountId32: {218 network: 'Any',219 id: alice.addressRaw,220 },221 }},222 },223 };224225 const assets = {226 V1: [227 {228 id: {229 Concrete: {230 parents: 0,231 interior: {232 X2: [233 {234 PalletInstance: STATEMINT_PALLET_INSTANCE,235 },236 {237 GeneralIndex: USDT_ASSET_ID,238 },239 ]},240 },241 },242 fun: {243 Fungible: TRANSFER_AMOUNT,244 },245 },246 ],247 };248249 const feeAssetItem = 0;250251 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);252 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');253254 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);255256 // common good parachain take commission in it native token257 console.log(258 '[Statemint -> Unique] transaction fees on Statemint: %s WND',259 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),260 );261 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;262263 });264265266 // ensure that asset has been delivered267 await helper.wait.newBlocks(3);268269 // expext collection id will be with id 1270 const free = await helper.ft.getBalance(1, {Substrate: alice.address});271272 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);273274 console.log(275 '[Statemint -> Unique] transaction fees on Unique: %s USDT',276 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),277 );278 console.log(279 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',280 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),281 );282 // commission has not paid in USDT token283 expect(free).to.be.equal(TRANSFER_AMOUNT);284 // ... and parachain native token285 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;286 });287288 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {289 const destination = {290 V2: {291 parents: 1,292 interior: {X2: [293 {294 Parachain: STATEMINT_CHAIN,295 },296 {297 AccountId32: {298 network: 'Any',299 id: alice.addressRaw,300 },301 },302 ]},303 },304 };305306 const relayFee = 400_000_000_000_000n;307 const currencies: [any, bigint][] = [308 [309 {310 ForeignAssetId: 0,311 },312 TRANSFER_AMOUNT,313 ],314 [315 {316 NativeAssetId: 'Parent',317 },318 relayFee,319 ],320 ];321322 const feeItem = 1;323324 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');325326 // the commission has been paid in parachain native token327 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);328 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));329 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;330331 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {332 await helper.wait.newBlocks(3);333334 // The USDT token never paid fees. Its amount not changed from begin value.335 // Also check that xcm transfer has been succeeded336 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;337 });338 });339340 itSub('Should connect and send Relay token to Unique', async ({helper}) => {341 balanceBobBefore = await helper.balance.getSubstrate(bob.address);342 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});343344 await usingRelayPlaygrounds(relayUrl, async (helper) => {345 const destination = {346 V1: {347 parents: 0,348 interior: {X1: {349 Parachain: UNIQUE_CHAIN,350 },351 },352 }};353354 const beneficiary = {355 V1: {356 parents: 0,357 interior: {X1: {358 AccountId32: {359 network: 'Any',360 id: bob.addressRaw,361 },362 }},363 },364 };365366 const assets = {367 V1: [368 {369 id: {370 Concrete: {371 parents: 0,372 interior: 'Here',373 },374 },375 fun: {376 Fungible: TRANSFER_AMOUNT_RELAY,377 },378 },379 ],380 };381382 const feeAssetItem = 0;383384 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');385 });386387 await helper.wait.newBlocks(3);388389 balanceBobAfter = await helper.balance.getSubstrate(bob.address);390 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});391392 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;393 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;394 console.log(395 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',396 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),397 );398 console.log(399 '[Relay (Westend) -> Unique] transaction fees: %s WND',400 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),401 );402 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);403 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;404 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;405 });406407 itSub('Should connect and send Relay token back', async ({helper}) => {408 let relayTokenBalanceBefore: bigint;409 let relayTokenBalanceAfter: bigint;410 await usingRelayPlaygrounds(relayUrl, async (helper) => {411 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);412 });413414 const destination = {415 V2: {416 parents: 1,417 interior: {418 X1:{419 AccountId32: {420 network: 'Any',421 id: bob.addressRaw,422 },423 },424 },425 },426 };427428 const currencies: any = [429 [430 {431 NativeAssetId: 'Parent',432 },433 TRANSFER_AMOUNT_RELAY,434 ],435 ];436437 const feeItem = 0;438439 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');440441 balanceBobFinal = await helper.balance.getSubstrate(bob.address);442 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));443444 await usingRelayPlaygrounds(relayUrl, async (helper) => {445 await helper.wait.newBlocks(10);446 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);447448 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;449 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));450 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;451 });452 });453});454455describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {456 let alice: IKeyringPair;457 let randomAccount: IKeyringPair;458459 let balanceUniqueTokenInit: bigint;460 let balanceUniqueTokenMiddle: bigint;461 let balanceUniqueTokenFinal: bigint;462 let balanceAcalaTokenInit: bigint;463 let balanceAcalaTokenMiddle: bigint;464 let balanceAcalaTokenFinal: bigint;465 let balanceUniqueForeignTokenInit: bigint;466 let balanceUniqueForeignTokenMiddle: bigint;467 let balanceUniqueForeignTokenFinal: bigint;468469 // computed by a test transfer from prod Unique to prod Acala.470 // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48471 // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)472 const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;473474 const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;475476 before(async () => {477 await usingPlaygrounds(async (helper, privateKey) => {478 alice = await privateKey('//Alice');479 [randomAccount] = await helper.arrange.createAccounts([0n], alice);480481 // Set the default version to wrap the first message to other chains.482 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);483 });484485 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {486 const destination = {487 V1: {488 parents: 1,489 interior: {490 X1: {491 Parachain: UNIQUE_CHAIN,492 },493 },494 },495 };496497 const metadata = {498 name: 'Unique Network',499 symbol: 'UNQ',500 decimals: 18,501 minimalBalance: 1250000000000000000n,502 };503504 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);505 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);506 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);507 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});508 });509510 await usingPlaygrounds(async (helper) => {511 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);512 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);513 });514 });515516 itSub('Should connect and send UNQ to Acala', async ({helper}) => {517518 const destination = {519 V2: {520 parents: 1,521 interior: {522 X1: {523 Parachain: ACALA_CHAIN,524 },525 },526 },527 };528529 const beneficiary = {530 V2: {531 parents: 0,532 interior: {533 X1: {534 AccountId32: {535 network: 'Any',536 id: randomAccount.addressRaw,537 },538 },539 },540 },541 };542543 const assets = {544 V2: [545 {546 id: {547 Concrete: {548 parents: 0,549 interior: 'Here',550 },551 },552 fun: {553 Fungible: TRANSFER_AMOUNT,554 },555 },556 ],557 };558559 const feeAssetItem = 0;560561 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');562 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);563564 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;565 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));566 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;567568 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {569 await helper.wait.newBlocks(3);570571 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});572 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);573574 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;575 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;576 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;577578 console.log(579 '[Unique -> Acala] transaction fees on Acala: %s ACA',580 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),581 );582 console.log(583 '[Unique -> Acala] transaction fees on Acala: %s UNQ',584 helper.util.bigIntToDecimals(acaUnqFees),585 );586 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));587 expect(acaFees == 0n).to.be.true;588 expect(589 acaUnqFees == expectedAcalaIncomeFee,590 'Acala took different income fee, check the Acala foreign asset config',591 ).to.be.true;592 });593 });594595 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {596 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {597 const destination = {598 V1: {599 parents: 1,600 interior: {601 X2: [602 {Parachain: UNIQUE_CHAIN},603 {604 AccountId32: {605 network: 'Any',606 id: randomAccount.addressRaw,607 },608 },609 ],610 },611 },612 };613614 const id = {615 ForeignAsset: 0,616 };617618 await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');619 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);620 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);621622 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;623 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;624625 console.log(626 '[Acala -> Unique] transaction fees on Acala: %s ACA',627 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),628 );629 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));630631 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;632 expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;633 });634635 await helper.wait.newBlocks(3);636637 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);638 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;639 expect(actuallyDelivered > 0).to.be.true;640641 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));642643 const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;644 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));645 expect(unqFees == 0n).to.be.true;646 });647});648649// These tests are relevant only when650// the the corresponding foreign assets are not registered651describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {652 let alice: IKeyringPair;653 let alith: IKeyringPair;654655 const testAmount = 100_000_000_000n;656 let uniqueParachainJunction;657 let uniqueAccountJunction;658659 let uniqueParachainMultilocation: any;660 let uniqueAccountMultilocation: any;661 let uniqueCombinedMultilocation: any;662663 before(async () => {664 await usingPlaygrounds(async (helper, privateKey) => {665 alice = await privateKey('//Alice');666667 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};668 uniqueAccountJunction = {669 AccountId32: {670 network: 'Any',671 id: alice.addressRaw,672 },673 };674675 uniqueParachainMultilocation = {676 V1: {677 parents: 1,678 interior: {679 X1: uniqueParachainJunction,680 },681 },682 };683684 uniqueAccountMultilocation = {685 V1: {686 parents: 0,687 interior: {688 X1: uniqueAccountJunction,689 },690 },691 };692693 uniqueCombinedMultilocation = {694 V1: {695 parents: 1,696 interior: {697 X2: [uniqueParachainJunction, uniqueAccountJunction],698 },699 },700 };701702 // Set the default version to wrap the first message to other chains.703 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);704 });705706 // eslint-disable-next-line require-await707 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {708 alith = helper.account.alithAccount();709 });710 });711712 const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {713 const maxWaitBlocks = 3;714715 const xcmpQueueFailEvent = await helper.wait.eventOutcome<XcmV2TraitsError>(716 maxWaitBlocks,717 'xcmpQueue',718 'Fail',719 );720721 expect(722 xcmpQueueFailEvent != null,723 `[reject ${network} tokens] 'xcmpQueue.FailEvent' event is expected`,724 ).to.be.true;725726 expect(727 xcmpQueueFailEvent!.isFailedToTransactAsset,728 `[reject ${network} tokens] The XCM error should be 'FailedToTransactAsset'`,729 ).to.be.true;730 };731732 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {733 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {734 const id = {735 Token: 'ACA',736 };737 const destination = uniqueCombinedMultilocation;738 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');739 });740741 await expectFailedToTransact('ACA', helper);742 });743744 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {745 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {746 const id = 'SelfReserve';747 const destination = uniqueCombinedMultilocation;748 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');749 });750751 await expectFailedToTransact('GLMR', helper);752 });753754 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {755 await usingAstarPlaygrounds(astarUrl, async (helper) => {756 const destinationParachain = uniqueParachainMultilocation;757 const beneficiary = uniqueAccountMultilocation;758 const assets = {759 V1: [{760 id: {761 Concrete: {762 parents: 0,763 interior: 'Here',764 },765 },766 fun: {767 Fungible: testAmount,768 },769 }],770 };771 const feeAssetItem = 0;772773 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [774 destinationParachain,775 beneficiary,776 assets,777 feeAssetItem,778 ]);779 });780781 await expectFailedToTransact('ASTR', helper);782 });783});784785describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {786 // Unique constants787 let uniqueDonor: IKeyringPair;788 let uniqueAssetLocation;789790 let randomAccountUnique: IKeyringPair;791 let randomAccountMoonbeam: IKeyringPair;792793 // Moonbeam constants794 let assetId: string;795796 const councilVotingThreshold = 2;797 const technicalCommitteeThreshold = 2;798 const votingPeriod = 3;799 const delayPeriod = 0;800801 const uniqueAssetMetadata = {802 name: 'xcUnique',803 symbol: 'xcUNQ',804 decimals: 18,805 isFrozen: false,806 minimalBalance: 1n,807 };808809 let balanceUniqueTokenInit: bigint;810 let balanceUniqueTokenMiddle: bigint;811 let balanceUniqueTokenFinal: bigint;812 let balanceForeignUnqTokenInit: bigint;813 let balanceForeignUnqTokenMiddle: bigint;814 let balanceForeignUnqTokenFinal: bigint;815 let balanceGlmrTokenInit: bigint;816 let balanceGlmrTokenMiddle: bigint;817 let balanceGlmrTokenFinal: bigint;818819 before(async () => {820 await usingPlaygrounds(async (helper, privateKey) => {821 uniqueDonor = await privateKey('//Alice');822 [randomAccountUnique] = await helper.arrange.createAccounts([0n], uniqueDonor);823824 balanceForeignUnqTokenInit = 0n;825826 // Set the default version to wrap the first message to other chains.827 const alice = uniqueDonor;828 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);829 });830831 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {832 const alithAccount = helper.account.alithAccount();833 const baltatharAccount = helper.account.baltatharAccount();834 const dorothyAccount = helper.account.dorothyAccount();835836 randomAccountMoonbeam = helper.account.create();837838 // >>> Sponsoring Dorothy >>>839 console.log('Sponsoring Dorothy.......');840 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);841 console.log('Sponsoring Dorothy.......DONE');842 // <<< Sponsoring Dorothy <<<843844 uniqueAssetLocation = {845 XCM: {846 parents: 1,847 interior: {X1: {Parachain: UNIQUE_CHAIN}},848 },849 };850 const existentialDeposit = 1n;851 const isSufficient = true;852 const unitsPerSecond = 1n;853 const numAssetsWeightHint = 0;854855 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({856 location: uniqueAssetLocation,857 metadata: uniqueAssetMetadata,858 existentialDeposit,859 isSufficient,860 unitsPerSecond,861 numAssetsWeightHint,862 });863 const proposalHash = blake2AsHex(encodedProposal);864865 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);866 console.log('Encoded length %d', encodedProposal.length);867 console.log('Encoded proposal hash for batch utility after schedule is %s', proposalHash);868869 // >>> Note motion preimage >>>870 console.log('Note motion preimage.......');871 await helper.democracy.notePreimage(baltatharAccount, encodedProposal);872 console.log('Note motion preimage.......DONE');873 // <<< Note motion preimage <<<874875 // >>> Propose external motion through council >>>876 console.log('Propose external motion through council.......');877 const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});878 const encodedMotion = externalMotion?.method.toHex() || '';879 const motionHash = blake2AsHex(encodedMotion);880 console.log('Motion hash is %s', motionHash);881882 await helper.collective.council.propose(baltatharAccount, councilVotingThreshold, externalMotion, externalMotion.encodedLength);883884 const councilProposalIdx = await helper.collective.council.proposalCount() - 1;885 await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);886 await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);887888 await helper.collective.council.close(889 dorothyAccount,890 motionHash,891 councilProposalIdx,892 {893 refTime: 1_000_000_000,894 proofSize: 1_000_000,895 },896 externalMotion.encodedLength,897 );898 console.log('Propose external motion through council.......DONE');899 // <<< Propose external motion through council <<<900901 // >>> Fast track proposal through technical committee >>>902 console.log('Fast track proposal through technical committee.......');903 const fastTrack = helper.democracy.fastTrack(proposalHash, votingPeriod, delayPeriod);904 const encodedFastTrack = fastTrack?.method.toHex() || '';905 const fastTrackHash = blake2AsHex(encodedFastTrack);906 console.log('FastTrack hash is %s', fastTrackHash);907908 await helper.collective.techCommittee.propose(alithAccount, technicalCommitteeThreshold, fastTrack, fastTrack.encodedLength);909910 const techProposalIdx = await helper.collective.techCommittee.proposalCount() - 1;911 await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);912 await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);913914 await helper.collective.techCommittee.close(915 baltatharAccount,916 fastTrackHash,917 techProposalIdx,918 {919 refTime: 1_000_000_000,920 proofSize: 1_000_000,921 },922 fastTrack.encodedLength,923 );924 console.log('Fast track proposal through technical committee.......DONE');925 // <<< Fast track proposal through technical committee <<<926927 // >>> Referendum voting >>>928 console.log('Referendum voting.......');929 await helper.democracy.referendumVote(dorothyAccount, 0, {930 balance: 10_000_000_000_000_000_000n,931 vote: {aye: true, conviction: 1},932 });933 console.log('Referendum voting.......DONE');934 // <<< Referendum voting <<<935936 // >>> Acquire Unique AssetId Info on Moonbeam >>>937 console.log('Acquire Unique AssetId Info on Moonbeam.......');938939 // Wait for the democracy execute940 await helper.wait.newBlocks(5);941942 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();943944 console.log('UNQ asset ID is %s', assetId);945 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');946 // >>> Acquire Unique AssetId Info on Moonbeam >>>947948 // >>> Sponsoring random Account >>>949 console.log('Sponsoring random Account.......');950 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);951 console.log('Sponsoring random Account.......DONE');952 // <<< Sponsoring random Account <<<953954 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);955 });956957 await usingPlaygrounds(async (helper) => {958 await helper.balance.transferToSubstrate(uniqueDonor, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);959 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);960 });961 });962963 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {964 const currencyId = {965 NativeAssetId: 'Here',966 };967 const dest = {968 V2: {969 parents: 1,970 interior: {971 X2: [972 {Parachain: MOONBEAM_CHAIN},973 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},974 ],975 },976 },977 };978 const amount = TRANSFER_AMOUNT;979980 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');981982 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);983 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;984985 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;986 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));987 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;988989 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {990 await helper.wait.newBlocks(3);991992 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);993994 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;995 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));996 expect(glmrFees == 0n).to.be.true;997998 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;9991000 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1001 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1002 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1003 });1004 });10051006 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1007 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1008 const asset = {1009 V1: {1010 id: {1011 Concrete: {1012 parents: 1,1013 interior: {1014 X1: {Parachain: UNIQUE_CHAIN},1015 },1016 },1017 },1018 fun: {1019 Fungible: TRANSFER_AMOUNT,1020 },1021 },1022 };1023 const destination = {1024 V1: {1025 parents: 1,1026 interior: {1027 X2: [1028 {Parachain: UNIQUE_CHAIN},1029 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1030 ],1031 },1032 },1033 };10341035 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');10361037 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);10381039 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1040 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1041 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;10421043 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);10441045 expect(unqRandomAccountAsset).to.be.null;10461047 balanceForeignUnqTokenFinal = 0n;10481049 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1050 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1051 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1052 });10531054 await helper.wait.newBlocks(3);10551056 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1057 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1058 expect(actuallyDelivered > 0).to.be.true;10591060 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));10611062 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1063 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1064 expect(unqFees == 0n).to.be.true;1065 });1066});10671068describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1069 let alice: IKeyringPair;1070 let randomAccount: IKeyringPair;10711072 const UNQ_ASSET_ID_ON_SHIDEN = 1;1073 const UNQ_MINIMAL_BALANCE_ON_SHIDEN = 1n;10741075 // Unique -> Astar1076 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Shiden.1077 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1078 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1079 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Shiden takes a commision in foreign tokens10801081 // Astar -> Unique1082 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1083 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ10841085 let balanceAfterUniqueToAstarXCM: bigint;10861087 before(async () => {1088 await usingPlaygrounds(async (helper, privateKey) => {1089 alice = await privateKey('//Alice');1090 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1091 console.log('randomAccount', randomAccount.address);10921093 // Set the default version to wrap the first message to other chains.1094 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1095 });10961097 await usingAstarPlaygrounds(astarUrl, async (helper) => {1098 console.log('1. Create foreign asset and metadata');1099 // TODO update metadata with values from production1100 await helper.assets.create(1101 alice,1102 UNQ_ASSET_ID_ON_SHIDEN,1103 alice.address,1104 UNQ_MINIMAL_BALANCE_ON_SHIDEN,1105 );11061107 await helper.assets.setMetadata(1108 alice,1109 UNQ_ASSET_ID_ON_SHIDEN,1110 'Cross chain UNQ',1111 'xcUNQ',1112 Number(UNQ_DECIMALS),1113 );11141115 console.log('2. Register asset location on Astar');1116 const assetLocation = {1117 V1: {1118 parents: 1,1119 interior: {1120 X1: {1121 Parachain: UNIQUE_CHAIN,1122 },1123 },1124 },1125 };11261127 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_SHIDEN]);11281129 console.log('3. Set UNQ payment for XCM execution on Astar');1130 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);11311132 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1133 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1134 });1135 });11361137 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1138 const destination = {1139 V2: {1140 parents: 1,1141 interior: {1142 X1: {1143 Parachain: ASTAR_CHAIN,1144 },1145 },1146 },1147 };11481149 const beneficiary = {1150 V2: {1151 parents: 0,1152 interior: {1153 X1: {1154 AccountId32: {1155 network: 'Any',1156 id: randomAccount.addressRaw,1157 },1158 },1159 },1160 },1161 };11621163 const assets = {1164 V2: [1165 {1166 id: {1167 Concrete: {1168 parents: 0,1169 interior: 'Here',1170 },1171 },1172 fun: {1173 Fungible: unqToAstarTransferred,1174 },1175 },1176 ],1177 };11781179 // Initial balance is 100 UNQ1180 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1181 console.log(`Initial balance is: ${balanceBefore}`);11821183 const feeAssetItem = 0;1184 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');11851186 // Balance after reserve transfer is less than 901187 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1188 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1189 console.log(`Unique's UNQ commission is: ${balanceBefore-balanceAfterUniqueToAstarXCM}`);1190 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;11911192 await usingAstarPlaygrounds(astarUrl, async (helper) => {1193 await helper.wait.newBlocks(3);1194 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_SHIDEN, randomAccount.address);1195 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);11961197 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1198 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred-xcUNQbalance!}`);11991200 expect(xcUNQbalance).to.eq(unqToAstarArrived);1201 // Astar balance does not changed1202 expect(astarBalance).to.eq(astarInitialBalance);1203 });1204 });12051206 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1207 await usingAstarPlaygrounds(astarUrl, async (helper) => {1208 const destination = {1209 V1: {1210 parents: 1,1211 interior: {1212 X1: {1213 Parachain: UNIQUE_CHAIN,1214 },1215 },1216 },1217 };12181219 const beneficiary = {1220 V1: {1221 parents: 0,1222 interior: {1223 X1: {1224 AccountId32: {1225 network: 'Any',1226 id: randomAccount.addressRaw,1227 },1228 },1229 },1230 },1231 };12321233 const assets = {1234 V1: [1235 {1236 id: {1237 Concrete: {1238 parents: 1,1239 interior: {1240 X1: {1241 Parachain: UNIQUE_CHAIN,1242 },1243 },1244 },1245 },1246 fun: {1247 Fungible: unqFromAstarTransfered,1248 },1249 },1250 ],1251 };12521253 // Initial balance is 1 ASTR1254 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1255 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1256 expect(balanceASTRbefore).to.eq(astarInitialBalance);12571258 const feeAssetItem = 0;1259 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1260 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);12611262 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_SHIDEN, randomAccount.address);1263 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1264 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);12651266 // Assert: xcUNQ balance correctly decreased1267 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1268 // Assert: ASTR balance is 0.996...1269 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1270 });12711272 await helper.wait.newBlocks(3);1273 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1274 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1275 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1276 });12771278 itSub.skip('Should not accept limitedReserveTransfer of UNQ from ASTAR', async ({helper}) => {1279 await usingAstarPlaygrounds(astarUrl, async (helper) => {1280 const destination = {1281 V1: {1282 parents: 1,1283 interior: {1284 X1: {1285 Parachain: UNIQUE_CHAIN,1286 },1287 },1288 },1289 };12901291 const beneficiary = {1292 V1: {1293 parents: 0,1294 interior: {1295 X1: {1296 AccountId32: {1297 network: 'Any',1298 id: randomAccount.addressRaw,1299 },1300 },1301 },1302 },1303 };13041305 const assets = {1306 V1: [1307 {1308 id: {1309 Concrete: {1310 parents: 1,1311 interior: {1312 X1: {1313 Parachain: UNIQUE_CHAIN,1314 },1315 },1316 },1317 },1318 fun: {1319 Fungible: unqFromAstarTransfered,1320 },1321 },1322 ],1323 };13241325 // Initial balance is 1 ASTAR1326 expect(await helper.balance.getSubstrate(randomAccount.address)).to.eq(astarInitialBalance);13271328 const feeAssetItem = 0;1329 // TODO: expect rejected:1330 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');1331 });1332 });1333});