difftreelog
test(polkadex) added reserve malicious test
in: master
2 files changed
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 config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';21import {nToBigInt} from '@polkadot/util';2223const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);24const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);25const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);26const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);27const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);28const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);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;37const polkadexUrl = config.polkadexUrl;3839const RELAY_DECIMALS = 12;40const STATEMINT_DECIMALS = 12;41const ACALA_DECIMALS = 12;42const ASTAR_DECIMALS = 18n;43const UNQ_DECIMALS = 18n;4445const TRANSFER_AMOUNT = 2000000000000000000000000n;4647const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4849const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;5051const USDT_ASSET_ID = 100;52const USDT_ASSET_METADATA_DECIMALS = 18;53const USDT_ASSET_METADATA_NAME = 'USDT';54const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';55const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;56const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5758const SAFE_XCM_VERSION = 2;5960describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {61 let alice: IKeyringPair;62 let bob: IKeyringPair;6364 let balanceStmnBefore: bigint;65 let balanceStmnAfter: bigint;6667 let balanceUniqueBefore: bigint;68 let balanceUniqueAfter: bigint;69 let balanceUniqueFinal: bigint;7071 let balanceBobBefore: bigint;72 let balanceBobAfter: bigint;73 let balanceBobFinal: bigint;7475 let balanceBobRelayTokenBefore: bigint;76 let balanceBobRelayTokenAfter: bigint;777879 before(async () => {80 await usingPlaygrounds(async (helper, privateKey) => {81 alice = await privateKey('//Alice');82 bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8384 // Set the default version to wrap the first message to other chains.85 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);86 });8788 await usingRelayPlaygrounds(relayUrl, async (helper) => {89 // Fund accounts on Statemint90 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);91 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);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 V2: {160 parents: 0,161 interior: {X1: {162 Parachain: UNIQUE_CHAIN,163 },164 },165 }};166167 const beneficiary = {168 V2: {169 parents: 0,170 interior: {X1: {171 AccountId32: {172 network: 'Any',173 id: alice.addressRaw,174 },175 }},176 },177 };178179 const assets = {180 V2: [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 V2: {206 parents: 1,207 interior: {X1: {208 Parachain: UNIQUE_CHAIN,209 },210 },211 }};212213 const beneficiary = {214 V2: {215 parents: 0,216 interior: {X1: {217 AccountId32: {218 network: 'Any',219 id: alice.addressRaw,220 },221 }},222 },223 };224225 const assets = {226 V2: [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 V2: {347 parents: 0,348 interior: {X1: {349 Parachain: UNIQUE_CHAIN,350 },351 },352 }};353354 const beneficiary = {355 V2: {356 parents: 0,357 interior: {X1: {358 AccountId32: {359 network: 'Any',360 id: bob.addressRaw,361 },362 }},363 },364 };365366 const assets = {367 V2: [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;473 const acalaEps = 8n * 10n ** 16n;474475 let acalaBackwardTransferAmount: bigint;476477 before(async () => {478 await usingPlaygrounds(async (helper, privateKey) => {479 alice = await privateKey('//Alice');480 [randomAccount] = await helper.arrange.createAccounts([0n], alice);481482 // Set the default version to wrap the first message to other chains.483 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);484 });485486 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {487 const destination = {488 V2: {489 parents: 1,490 interior: {491 X1: {492 Parachain: UNIQUE_CHAIN,493 },494 },495 },496 };497498 const metadata = {499 name: 'Unique Network',500 symbol: 'UNQ',501 decimals: 18,502 minimalBalance: 1250000000000000000n,503 };504505 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);506 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);507 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);508 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});509 });510511 await usingPlaygrounds(async (helper) => {512 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);513 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);514 });515 });516517 itSub('Should connect and send UNQ to Acala', async ({helper}) => {518519 const destination = {520 V2: {521 parents: 1,522 interior: {523 X1: {524 Parachain: ACALA_CHAIN,525 },526 },527 },528 };529530 const beneficiary = {531 V2: {532 parents: 0,533 interior: {534 X1: {535 AccountId32: {536 network: 'Any',537 id: randomAccount.addressRaw,538 },539 },540 },541 },542 };543544 const assets = {545 V2: [546 {547 id: {548 Concrete: {549 parents: 0,550 interior: 'Here',551 },552 },553 fun: {554 Fungible: TRANSFER_AMOUNT,555 },556 },557 ],558 };559560 const feeAssetItem = 0;561562 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');563 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);564565 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;566 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));567 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;568569 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {570 await helper.wait.newBlocks(3);571572 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});573 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);574575 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;576 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;577 acalaBackwardTransferAmount = unqIncomeTransfer;578579 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;580581 console.log(582 '[Unique -> Acala] transaction fees on Acala: %s ACA',583 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),584 );585 console.log(586 '[Unique -> Acala] transaction fees on Acala: %s UNQ',587 helper.util.bigIntToDecimals(acaUnqFees),588 );589 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));590 expect(acaFees == 0n).to.be.true;591592 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;593594 expect(595 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,596 'Acala took different income fee, check the Acala foreign asset config',597 ).to.be.true;598 });599 });600601 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {602 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {603 const destination = {604 V2: {605 parents: 1,606 interior: {607 X2: [608 {Parachain: UNIQUE_CHAIN},609 {610 AccountId32: {611 network: 'Any',612 id: randomAccount.addressRaw,613 },614 },615 ],616 },617 },618 };619620 const id = {621 ForeignAsset: 0,622 };623624 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');625 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);626 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);627628 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;629 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;630631 console.log(632 '[Acala -> Unique] transaction fees on Acala: %s ACA',633 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),634 );635 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));636637 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;638 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;639 });640641 await helper.wait.newBlocks(3);642643 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);644 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;645 expect(actuallyDelivered > 0).to.be.true;646647 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));648649 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;650 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));651 expect(unqFees == 0n).to.be.true;652 });653654 itSub('Acala can send only up to its balance', async ({helper}) => {655 // set Acala's sovereign account's balance656 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);657 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);658 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);659660 const moreThanAcalaHas = acalaBalance * 2n;661662 let targetAccountBalance = 0n;663 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);664665 const uniqueMultilocation = {666 V2: {667 parents: 1,668 interior: {669 X1: {Parachain: UNIQUE_CHAIN},670 },671 },672 };673674 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(675 targetAccount.addressRaw,676 {677 Concrete: {678 parents: 0,679 interior: 'Here',680 },681 },682 moreThanAcalaHas,683 );684685 let maliciousXcmProgramSent: any;686 const maxWaitBlocks = 3;687688 // Try to trick Unique689 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {690 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);691692 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);693 });694695 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash696 && event.outcome.isFailedToTransactAsset);697698 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);699 expect(targetAccountBalance).to.be.equal(0n);700701 // But Acala still can send the correct amount702 const validTransferAmount = acalaBalance / 2n;703 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(704 targetAccount.addressRaw,705 {706 Concrete: {707 parents: 0,708 interior: 'Here',709 },710 },711 validTransferAmount,712 );713714 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {715 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);716 });717718 await helper.wait.newBlocks(maxWaitBlocks);719720 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);721 expect(targetAccountBalance).to.be.equal(validTransferAmount);722 });723724 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {725 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);726 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);727728 const uniqueMultilocation = {729 V2: {730 parents: 1,731 interior: {732 X1: {733 Parachain: UNIQUE_CHAIN,734 },735 },736 },737 };738739 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(740 targetAccount.addressRaw,741 {742 Concrete: {743 parents: 1,744 interior: {745 X1: {746 Parachain: UNIQUE_CHAIN,747 },748 },749 },750 },751 testAmount,752 );753754 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(755 targetAccount.addressRaw,756 {757 Concrete: {758 parents: 0,759 interior: 'Here',760 },761 },762 testAmount,763 );764765 let maliciousXcmProgramFullIdSent: any;766 let maliciousXcmProgramHereIdSent: any;767 const maxWaitBlocks = 3;768769 // Try to trick Unique using full UNQ identification770 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {771 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);772773 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);774 });775776 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash777 && event.outcome.isUntrustedReserveLocation);778779 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);780 expect(accountBalance).to.be.equal(0n);781782 // Try to trick Unique using shortened UNQ identification783 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {784 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);785786 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);787 });788789 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash790 && event.outcome.isUntrustedReserveLocation);791792 accountBalance = await helper.balance.getSubstrate(targetAccount.address);793 expect(accountBalance).to.be.equal(0n);794 });795});796797describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {798 let alice: IKeyringPair;799 let randomAccount: IKeyringPair;800 let unqFees: bigint;801 let balanceUniqueTokenInit: bigint;802 let balanceUniqueTokenMiddle: bigint;803 let balanceUniqueTokenFinal: bigint;804 const maxWaitBlocks = 6;805806 const uniqueAssetId = {807 Concrete: {808 parents: 1,809 interior: {810 X1: {811 Parachain: UNIQUE_CHAIN,812 },813 },814 },815 };816817 before(async () => {818 await usingPlaygrounds(async (helper, privateKey) => {819 alice = await privateKey('//Alice');820 [randomAccount] = await helper.arrange.createAccounts([0n], alice);821822 // Set the default version to wrap the first message to other chains.823 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);824 });825826 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {827 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])).toJSON() as [])828 .map(nToBigInt).length != 0;829830 if(!isWhitelisted) {831 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);832 }833834 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);835 });836837 await usingPlaygrounds(async (helper) => {838 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);839 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);840 });841 });842843 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {844845 const destination = {846 V2: {847 parents: 1,848 interior: {849 X1: {850 Parachain: POLKADEX_CHAIN,851 },852 },853 },854 };855856 const beneficiary = {857 V2: {858 parents: 0,859 interior: {860 X1: {861 AccountId32: {862 network: 'Any',863 id: randomAccount.addressRaw,864 },865 },866 },867 },868 };869870 const assets = {871 V2: [872 {873 id: {874 Concrete: {875 parents: 0,876 interior: 'Here',877 },878 },879 fun: {880 Fungible: TRANSFER_AMOUNT,881 },882 },883 ],884 };885886 const feeAssetItem = 0;887888 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');889 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);890 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);891892 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;893 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));894 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;895896 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {897 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);898 });899 });900901902 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {903904 const uniqueMultilocation = {905 V2: {906 parents: 1,907 interior: {908 X1: {Parachain: UNIQUE_CHAIN},909 },910 },911 };912913 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(914 randomAccount.addressRaw,915 {916 Concrete: {917 parents: 0,918 interior: 'Here',919 },920 },921 TRANSFER_AMOUNT,922 );923924 let xcmProgramSent: any;925926927 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {928 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);929930 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);931 });932933 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);934935 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);936937 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);938 });939940 itSub('Polkadex can send only up to its balance', async ({helper}) => {941 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);942 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);943 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);944 const moreThanPolkadexHas = 2n * polkadexBalance;945946 const targetAccount = helper.arrange.createEmptyAccount();947948 const uniqueMultilocation = {949 V2: {950 parents: 1,951 interior: {952 X1: {Parachain: UNIQUE_CHAIN},953 },954 },955 };956957 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(958 targetAccount.addressRaw,959 {960 Concrete: {961 parents: 0,962 interior: 'Here',963 },964 },965 moreThanPolkadexHas,966 );967968 let maliciousXcmProgramSent: any;969970971 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {972 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);973974 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);975 });976977 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash);978979 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);980 expect(targetAccountBalance).to.be.equal(0n);981 });982});983984// These tests are relevant only when985// the the corresponding foreign assets are not registered986describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {987 let alice: IKeyringPair;988 let alith: IKeyringPair;989990 const testAmount = 100_000_000_000n;991 let uniqueParachainJunction;992 let uniqueAccountJunction;993994 let uniqueParachainMultilocation: any;995 let uniqueAccountMultilocation: any;996 let uniqueCombinedMultilocation: any;997 let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2998999 let messageSent: any;10001001 const maxWaitBlocks = 3;10021003 before(async () => {1004 await usingPlaygrounds(async (helper, privateKey) => {1005 alice = await privateKey('//Alice');10061007 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1008 uniqueAccountJunction = {1009 AccountId32: {1010 network: 'Any',1011 id: alice.addressRaw,1012 },1013 };10141015 uniqueParachainMultilocation = {1016 V2: {1017 parents: 1,1018 interior: {1019 X1: uniqueParachainJunction,1020 },1021 },1022 };10231024 uniqueAccountMultilocation = {1025 V2: {1026 parents: 0,1027 interior: {1028 X1: uniqueAccountJunction,1029 },1030 },1031 };10321033 uniqueCombinedMultilocation = {1034 V2: {1035 parents: 1,1036 interior: {1037 X2: [uniqueParachainJunction, uniqueAccountJunction],1038 },1039 },1040 };10411042 uniqueCombinedMultilocationAcala = {1043 V2: {1044 parents: 1,1045 interior: {1046 X2: [uniqueParachainJunction, uniqueAccountJunction],1047 },1048 },1049 };10501051 // Set the default version to wrap the first message to other chains.1052 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1053 });10541055 // eslint-disable-next-line require-await1056 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1057 alith = helper.account.alithAccount();1058 });1059 });10601061 const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {1062 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash1063 && event.outcome.isFailedToTransactAsset);1064 };10651066 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1067 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1068 const id = {1069 Token: 'ACA',1070 };1071 const destination = uniqueCombinedMultilocationAcala;1072 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');10731074 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1075 });10761077 await expectFailedToTransact(helper, messageSent);1078 });10791080 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1081 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1082 const id = 'SelfReserve';1083 const destination = uniqueCombinedMultilocation;1084 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');10851086 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1087 });10881089 await expectFailedToTransact(helper, messageSent);1090 });10911092 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1093 await usingAstarPlaygrounds(astarUrl, async (helper) => {1094 const destinationParachain = uniqueParachainMultilocation;1095 const beneficiary = uniqueAccountMultilocation;1096 const assets = {1097 V2: [{1098 id: {1099 Concrete: {1100 parents: 0,1101 interior: 'Here',1102 },1103 },1104 fun: {1105 Fungible: testAmount,1106 },1107 }],1108 };1109 const feeAssetItem = 0;11101111 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1112 destinationParachain,1113 beneficiary,1114 assets,1115 feeAssetItem,1116 ]);11171118 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1119 });11201121 await expectFailedToTransact(helper, messageSent);1122 });11231124 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {11251126 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1127 helper.arrange.createEmptyAccount().addressRaw,1128 {1129 Concrete: {1130 parents: 1,1131 interior: {1132 X1: {1133 Parachain: POLKADEX_CHAIN,1134 },1135 },1136 },1137 },1138 testAmount,1139 );11401141 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1142 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1143 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1144 });11451146 await expectFailedToTransact(helper, messageSent);1147 });1148});11491150describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1151 // Unique constants1152 let alice: IKeyringPair;1153 let uniqueAssetLocation;11541155 let randomAccountUnique: IKeyringPair;1156 let randomAccountMoonbeam: IKeyringPair;11571158 // Moonbeam constants1159 let assetId: string;11601161 const uniqueAssetMetadata = {1162 name: 'xcUnique',1163 symbol: 'xcUNQ',1164 decimals: 18,1165 isFrozen: false,1166 minimalBalance: 1n,1167 };11681169 let balanceUniqueTokenInit: bigint;1170 let balanceUniqueTokenMiddle: bigint;1171 let balanceUniqueTokenFinal: bigint;1172 let balanceForeignUnqTokenInit: bigint;1173 let balanceForeignUnqTokenMiddle: bigint;1174 let balanceForeignUnqTokenFinal: bigint;1175 let balanceGlmrTokenInit: bigint;1176 let balanceGlmrTokenMiddle: bigint;1177 let balanceGlmrTokenFinal: bigint;11781179 before(async () => {1180 await usingPlaygrounds(async (helper, privateKey) => {1181 alice = await privateKey('//Alice');1182 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);11831184 balanceForeignUnqTokenInit = 0n;11851186 // Set the default version to wrap the first message to other chains.1187 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1188 });11891190 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1191 const alithAccount = helper.account.alithAccount();1192 const baltatharAccount = helper.account.baltatharAccount();1193 const dorothyAccount = helper.account.dorothyAccount();11941195 randomAccountMoonbeam = helper.account.create();11961197 // >>> Sponsoring Dorothy >>>1198 console.log('Sponsoring Dorothy.......');1199 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1200 console.log('Sponsoring Dorothy.......DONE');1201 // <<< Sponsoring Dorothy <<<12021203 uniqueAssetLocation = {1204 XCM: {1205 parents: 1,1206 interior: {X1: {Parachain: UNIQUE_CHAIN}},1207 },1208 };1209 const existentialDeposit = 1n;1210 const isSufficient = true;1211 const unitsPerSecond = 1n;1212 const numAssetsWeightHint = 0;12131214 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1215 location: uniqueAssetLocation,1216 metadata: uniqueAssetMetadata,1217 existentialDeposit,1218 isSufficient,1219 unitsPerSecond,1220 numAssetsWeightHint,1221 });12221223 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);12241225 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);12261227 // >>> Acquire Unique AssetId Info on Moonbeam >>>1228 console.log('Acquire Unique AssetId Info on Moonbeam.......');12291230 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();12311232 console.log('UNQ asset ID is %s', assetId);1233 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1234 // >>> Acquire Unique AssetId Info on Moonbeam >>>12351236 // >>> Sponsoring random Account >>>1237 console.log('Sponsoring random Account.......');1238 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1239 console.log('Sponsoring random Account.......DONE');1240 // <<< Sponsoring random Account <<<12411242 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1243 });12441245 await usingPlaygrounds(async (helper) => {1246 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1247 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1248 });1249 });12501251 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1252 const currencyId = {1253 NativeAssetId: 'Here',1254 };1255 const dest = {1256 V2: {1257 parents: 1,1258 interior: {1259 X2: [1260 {Parachain: MOONBEAM_CHAIN},1261 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1262 ],1263 },1264 },1265 };1266 const amount = TRANSFER_AMOUNT;12671268 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');12691270 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1271 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;12721273 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1274 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1275 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;12761277 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1278 await helper.wait.newBlocks(3);12791280 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);12811282 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1283 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1284 expect(glmrFees == 0n).to.be.true;12851286 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;12871288 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1289 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1290 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1291 });1292 });12931294 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1295 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1296 const asset = {1297 V2: {1298 id: {1299 Concrete: {1300 parents: 1,1301 interior: {1302 X1: {Parachain: UNIQUE_CHAIN},1303 },1304 },1305 },1306 fun: {1307 Fungible: TRANSFER_AMOUNT,1308 },1309 },1310 };1311 const destination = {1312 V2: {1313 parents: 1,1314 interior: {1315 X2: [1316 {Parachain: UNIQUE_CHAIN},1317 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1318 ],1319 },1320 },1321 };13221323 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');13241325 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);13261327 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1328 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1329 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;13301331 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);13321333 expect(unqRandomAccountAsset).to.be.null;13341335 balanceForeignUnqTokenFinal = 0n;13361337 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1338 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1339 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1340 });13411342 await helper.wait.newBlocks(3);13431344 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1345 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1346 expect(actuallyDelivered > 0).to.be.true;13471348 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));13491350 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1351 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1352 expect(unqFees == 0n).to.be.true;1353 });13541355 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1356 // set Moonbeam's sovereign account's balance1357 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1358 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1359 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);13601361 const moreThanMoonbeamHas = moonbeamBalance * 2n;13621363 let targetAccountBalance = 0n;1364 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);13651366 const uniqueMultilocation = {1367 V2: {1368 parents: 1,1369 interior: {1370 X1: {Parachain: UNIQUE_CHAIN},1371 },1372 },1373 };13741375 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1376 targetAccount.addressRaw,1377 {1378 Concrete: {1379 parents: 0,1380 interior: 'Here',1381 },1382 },1383 moreThanMoonbeamHas,1384 );13851386 let maliciousXcmProgramSent: any;1387 const maxWaitBlocks = 3;13881389 // Try to trick Unique1390 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1391 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);13921393 // Needed to bypass the call filter.1394 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1395 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);13961397 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1398 });13991400 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1401 && event.outcome.isFailedToTransactAsset);14021403 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1404 expect(targetAccountBalance).to.be.equal(0n);14051406 // But Moonbeam still can send the correct amount1407 const validTransferAmount = moonbeamBalance / 2n;1408 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1409 targetAccount.addressRaw,1410 {1411 Concrete: {1412 parents: 0,1413 interior: 'Here',1414 },1415 },1416 validTransferAmount,1417 );14181419 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1420 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);14211422 // Needed to bypass the call filter.1423 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1424 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1425 });14261427 await helper.wait.newBlocks(maxWaitBlocks);14281429 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1430 expect(targetAccountBalance).to.be.equal(validTransferAmount);1431 });14321433 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1434 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1435 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);14361437 const uniqueMultilocation = {1438 V2: {1439 parents: 1,1440 interior: {1441 X1: {1442 Parachain: UNIQUE_CHAIN,1443 },1444 },1445 },1446 };14471448 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1449 targetAccount.addressRaw,1450 {1451 Concrete: {1452 parents: 0,1453 interior: {1454 X1: {1455 Parachain: UNIQUE_CHAIN,1456 },1457 },1458 },1459 },1460 testAmount,1461 );14621463 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1464 targetAccount.addressRaw,1465 {1466 Concrete: {1467 parents: 0,1468 interior: 'Here',1469 },1470 },1471 testAmount,1472 );14731474 let maliciousXcmProgramFullIdSent: any;1475 let maliciousXcmProgramHereIdSent: any;1476 const maxWaitBlocks = 3;14771478 // Try to trick Unique using full UNQ identification1479 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1480 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);14811482 // Needed to bypass the call filter.1483 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1484 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);14851486 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1487 });14881489 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1490 && event.outcome.isUntrustedReserveLocation);14911492 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1493 expect(accountBalance).to.be.equal(0n);14941495 // Try to trick Unique using shortened UNQ identification1496 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1497 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);14981499 // Needed to bypass the call filter.1500 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1501 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15021503 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1504 });15051506 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1507 && event.outcome.isUntrustedReserveLocation);15081509 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1510 expect(accountBalance).to.be.equal(0n);1511 });1512});15131514describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1515 let alice: IKeyringPair;1516 let randomAccount: IKeyringPair;15171518 const UNQ_ASSET_ID_ON_ASTAR = 1;1519 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;15201521 // Unique -> Astar1522 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1523 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1524 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1525 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens15261527 // Astar -> Unique1528 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1529 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ15301531 let balanceAfterUniqueToAstarXCM: bigint;15321533 before(async () => {1534 await usingPlaygrounds(async (helper, privateKey) => {1535 alice = await privateKey('//Alice');1536 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1537 console.log('randomAccount', randomAccount.address);15381539 // Set the default version to wrap the first message to other chains.1540 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1541 });15421543 await usingAstarPlaygrounds(astarUrl, async (helper) => {1544 console.log('1. Create foreign asset and metadata');1545 // TODO update metadata with values from production1546 await helper.assets.create(1547 alice,1548 UNQ_ASSET_ID_ON_ASTAR,1549 alice.address,1550 UNQ_MINIMAL_BALANCE_ON_ASTAR,1551 );15521553 await helper.assets.setMetadata(1554 alice,1555 UNQ_ASSET_ID_ON_ASTAR,1556 'Cross chain UNQ',1557 'xcUNQ',1558 Number(UNQ_DECIMALS),1559 );15601561 console.log('2. Register asset location on Astar');1562 const assetLocation = {1563 V2: {1564 parents: 1,1565 interior: {1566 X1: {1567 Parachain: UNIQUE_CHAIN,1568 },1569 },1570 },1571 };15721573 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);15741575 console.log('3. Set UNQ payment for XCM execution on Astar');1576 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);15771578 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1579 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1580 });1581 });15821583 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1584 const destination = {1585 V2: {1586 parents: 1,1587 interior: {1588 X1: {1589 Parachain: ASTAR_CHAIN,1590 },1591 },1592 },1593 };15941595 const beneficiary = {1596 V2: {1597 parents: 0,1598 interior: {1599 X1: {1600 AccountId32: {1601 network: 'Any',1602 id: randomAccount.addressRaw,1603 },1604 },1605 },1606 },1607 };16081609 const assets = {1610 V2: [1611 {1612 id: {1613 Concrete: {1614 parents: 0,1615 interior: 'Here',1616 },1617 },1618 fun: {1619 Fungible: unqToAstarTransferred,1620 },1621 },1622 ],1623 };16241625 // Initial balance is 100 UNQ1626 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1627 console.log(`Initial balance is: ${balanceBefore}`);16281629 const feeAssetItem = 0;1630 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');16311632 // Balance after reserve transfer is less than 901633 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1634 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1635 console.log(`Unique's UNQ commission is: ${balanceBefore-balanceAfterUniqueToAstarXCM}`);1636 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;16371638 await usingAstarPlaygrounds(astarUrl, async (helper) => {1639 await helper.wait.newBlocks(3);1640 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1641 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);16421643 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1644 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred-xcUNQbalance!}`);16451646 expect(xcUNQbalance).to.eq(unqToAstarArrived);1647 // Astar balance does not changed1648 expect(astarBalance).to.eq(astarInitialBalance);1649 });1650 });16511652 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1653 await usingAstarPlaygrounds(astarUrl, async (helper) => {1654 const destination = {1655 V2: {1656 parents: 1,1657 interior: {1658 X1: {1659 Parachain: UNIQUE_CHAIN,1660 },1661 },1662 },1663 };16641665 const beneficiary = {1666 V2: {1667 parents: 0,1668 interior: {1669 X1: {1670 AccountId32: {1671 network: 'Any',1672 id: randomAccount.addressRaw,1673 },1674 },1675 },1676 },1677 };16781679 const assets = {1680 V2: [1681 {1682 id: {1683 Concrete: {1684 parents: 1,1685 interior: {1686 X1: {1687 Parachain: UNIQUE_CHAIN,1688 },1689 },1690 },1691 },1692 fun: {1693 Fungible: unqFromAstarTransfered,1694 },1695 },1696 ],1697 };16981699 // Initial balance is 1 ASTR1700 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1701 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1702 expect(balanceASTRbefore).to.eq(astarInitialBalance);17031704 const feeAssetItem = 0;1705 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1706 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17071708 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1709 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1710 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17111712 // Assert: xcUNQ balance correctly decreased1713 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1714 // Assert: ASTR balance is 0.996...1715 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1716 });17171718 await helper.wait.newBlocks(3);1719 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1720 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1721 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1722 });17231724 itSub('Astar can send only up to its balance', async ({helper}) => {1725 // set Astar's sovereign account's balance1726 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1727 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1728 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);17291730 const moreThanAstarHas = astarBalance * 2n;17311732 let targetAccountBalance = 0n;1733 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);17341735 const uniqueMultilocation = {1736 V2: {1737 parents: 1,1738 interior: {1739 X1: {Parachain: UNIQUE_CHAIN},1740 },1741 },1742 };17431744 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1745 targetAccount.addressRaw,1746 {1747 Concrete: {1748 parents: 0,1749 interior: 'Here',1750 },1751 },1752 moreThanAstarHas,1753 );17541755 let maliciousXcmProgramSent: any;1756 const maxWaitBlocks = 3;17571758 // Try to trick Unique1759 await usingAstarPlaygrounds(astarUrl, async (helper) => {1760 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);17611762 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1763 });17641765 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1766 && event.outcome.isFailedToTransactAsset);17671768 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1769 expect(targetAccountBalance).to.be.equal(0n);17701771 // But Astar still can send the correct amount1772 const validTransferAmount = astarBalance / 2n;1773 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1774 targetAccount.addressRaw,1775 {1776 Concrete: {1777 parents: 0,1778 interior: 'Here',1779 },1780 },1781 validTransferAmount,1782 );17831784 await usingAstarPlaygrounds(astarUrl, async (helper) => {1785 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1786 });17871788 await helper.wait.newBlocks(maxWaitBlocks);17891790 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1791 expect(targetAccountBalance).to.be.equal(validTransferAmount);1792 });17931794 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1795 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1796 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);17971798 const uniqueMultilocation = {1799 V2: {1800 parents: 1,1801 interior: {1802 X1: {1803 Parachain: UNIQUE_CHAIN,1804 },1805 },1806 },1807 };18081809 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1810 targetAccount.addressRaw,1811 {1812 Concrete: {1813 parents: 1,1814 interior: {1815 X1: {1816 Parachain: UNIQUE_CHAIN,1817 },1818 },1819 },1820 },1821 testAmount,1822 );18231824 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1825 targetAccount.addressRaw,1826 {1827 Concrete: {1828 parents: 0,1829 interior: 'Here',1830 },1831 },1832 testAmount,1833 );18341835 let maliciousXcmProgramFullIdSent: any;1836 let maliciousXcmProgramHereIdSent: any;1837 const maxWaitBlocks = 3;18381839 // Try to trick Unique using full UNQ identification1840 await usingAstarPlaygrounds(astarUrl, async (helper) => {1841 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);18421843 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1844 });18451846 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1847 && event.outcome.isUntrustedReserveLocation);18481849 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1850 expect(accountBalance).to.be.equal(0n);18511852 // Try to trick Unique using shortened UNQ identification1853 await usingAstarPlaygrounds(astarUrl, async (helper) => {1854 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);18551856 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1857 });18581859 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1860 && event.outcome.isUntrustedReserveLocation);18611862 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1863 expect(accountBalance).to.be.equal(0n);1864 });1865});1// 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 config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';21import {nToBigInt} from '@polkadot/util';2223const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);24const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);25const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);26const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);27const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);28const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);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;37const polkadexUrl = config.polkadexUrl;3839const RELAY_DECIMALS = 12;40const STATEMINT_DECIMALS = 12;41const ACALA_DECIMALS = 12;42const ASTAR_DECIMALS = 18n;43const UNQ_DECIMALS = 18n;4445const TRANSFER_AMOUNT = 2000000000000000000000000n;4647const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4849const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;5051const USDT_ASSET_ID = 100;52const USDT_ASSET_METADATA_DECIMALS = 18;53const USDT_ASSET_METADATA_NAME = 'USDT';54const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';55const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;56const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5758const SAFE_XCM_VERSION = 2;59const maxWaitBlocks = 6;60const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {61 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash62 && event.outcome.isFailedToTransactAsset);63};64const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {65 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash66 && event.outcome.isUntrustedReserveLocation);67};68describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {69 let alice: IKeyringPair;70 let bob: IKeyringPair;7172 let balanceStmnBefore: bigint;73 let balanceStmnAfter: bigint;7475 let balanceUniqueBefore: bigint;76 let balanceUniqueAfter: bigint;77 let balanceUniqueFinal: bigint;7879 let balanceBobBefore: bigint;80 let balanceBobAfter: bigint;81 let balanceBobFinal: bigint;8283 let balanceBobRelayTokenBefore: bigint;84 let balanceBobRelayTokenAfter: bigint;858687 before(async () => {88 await usingPlaygrounds(async (helper, privateKey) => {89 alice = await privateKey('//Alice');90 bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor9192 // Set the default version to wrap the first message to other chains.93 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);94 });9596 await usingRelayPlaygrounds(relayUrl, async (helper) => {97 // Fund accounts on Statemint98 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);99 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);100 });101102 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {103 const sovereignFundingAmount = 3_500_000_000n;104105 await helper.assets.create(106 alice,107 USDT_ASSET_ID,108 alice.address,109 USDT_ASSET_METADATA_MINIMAL_BALANCE,110 );111 await helper.assets.setMetadata(112 alice,113 USDT_ASSET_ID,114 USDT_ASSET_METADATA_NAME,115 USDT_ASSET_METADATA_DESCRIPTION,116 USDT_ASSET_METADATA_DECIMALS,117 );118 await helper.assets.mint(119 alice,120 USDT_ASSET_ID,121 alice.address,122 USDT_ASSET_AMOUNT,123 );124125 // funding parachain sovereing account on Statemint.126 // The sovereign account should be created before any action127 // (the assets pallet on Statemint check if the sovereign account exists)128 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);129 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);130 });131132133 await usingPlaygrounds(async (helper) => {134 const location = {135 V2: {136 parents: 1,137 interior: {X3: [138 {139 Parachain: STATEMINT_CHAIN,140 },141 {142 PalletInstance: STATEMINT_PALLET_INSTANCE,143 },144 {145 GeneralIndex: USDT_ASSET_ID,146 },147 ]},148 },149 };150151 const metadata =152 {153 name: USDT_ASSET_ID,154 symbol: USDT_ASSET_METADATA_NAME,155 decimals: USDT_ASSET_METADATA_DECIMALS,156 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,157 };158 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);159 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);160 });161162163 // Providing the relay currency to the unique sender account164 // (fee for USDT XCM are paid in relay tokens)165 await usingRelayPlaygrounds(relayUrl, async (helper) => {166 const destination = {167 V2: {168 parents: 0,169 interior: {X1: {170 Parachain: UNIQUE_CHAIN,171 },172 },173 }};174175 const beneficiary = {176 V2: {177 parents: 0,178 interior: {X1: {179 AccountId32: {180 network: 'Any',181 id: alice.addressRaw,182 },183 }},184 },185 };186187 const assets = {188 V2: [189 {190 id: {191 Concrete: {192 parents: 0,193 interior: 'Here',194 },195 },196 fun: {197 Fungible: TRANSFER_AMOUNT_RELAY,198 },199 },200 ],201 };202203 const feeAssetItem = 0;204205 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');206 });207208 });209210 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {211 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {212 const dest = {213 V2: {214 parents: 1,215 interior: {X1: {216 Parachain: UNIQUE_CHAIN,217 },218 },219 }};220221 const beneficiary = {222 V2: {223 parents: 0,224 interior: {X1: {225 AccountId32: {226 network: 'Any',227 id: alice.addressRaw,228 },229 }},230 },231 };232233 const assets = {234 V2: [235 {236 id: {237 Concrete: {238 parents: 0,239 interior: {240 X2: [241 {242 PalletInstance: STATEMINT_PALLET_INSTANCE,243 },244 {245 GeneralIndex: USDT_ASSET_ID,246 },247 ]},248 },249 },250 fun: {251 Fungible: TRANSFER_AMOUNT,252 },253 },254 ],255 };256257 const feeAssetItem = 0;258259 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);260 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');261262 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);263264 // common good parachain take commission in it native token265 console.log(266 '[Statemint -> Unique] transaction fees on Statemint: %s WND',267 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),268 );269 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;270271 });272273274 // ensure that asset has been delivered275 await helper.wait.newBlocks(3);276277 // expext collection id will be with id 1278 const free = await helper.ft.getBalance(1, {Substrate: alice.address});279280 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);281282 console.log(283 '[Statemint -> Unique] transaction fees on Unique: %s USDT',284 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),285 );286 console.log(287 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',288 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),289 );290 // commission has not paid in USDT token291 expect(free).to.be.equal(TRANSFER_AMOUNT);292 // ... and parachain native token293 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;294 });295296 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {297 const destination = {298 V2: {299 parents: 1,300 interior: {X2: [301 {302 Parachain: STATEMINT_CHAIN,303 },304 {305 AccountId32: {306 network: 'Any',307 id: alice.addressRaw,308 },309 },310 ]},311 },312 };313314 const relayFee = 400_000_000_000_000n;315 const currencies: [any, bigint][] = [316 [317 {318 ForeignAssetId: 0,319 },320 TRANSFER_AMOUNT,321 ],322 [323 {324 NativeAssetId: 'Parent',325 },326 relayFee,327 ],328 ];329330 const feeItem = 1;331332 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');333334 // the commission has been paid in parachain native token335 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);336 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));337 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;338339 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {340 await helper.wait.newBlocks(3);341342 // The USDT token never paid fees. Its amount not changed from begin value.343 // Also check that xcm transfer has been succeeded344 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;345 });346 });347348 itSub('Should connect and send Relay token to Unique', async ({helper}) => {349 balanceBobBefore = await helper.balance.getSubstrate(bob.address);350 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});351352 await usingRelayPlaygrounds(relayUrl, async (helper) => {353 const destination = {354 V2: {355 parents: 0,356 interior: {X1: {357 Parachain: UNIQUE_CHAIN,358 },359 },360 }};361362 const beneficiary = {363 V2: {364 parents: 0,365 interior: {X1: {366 AccountId32: {367 network: 'Any',368 id: bob.addressRaw,369 },370 }},371 },372 };373374 const assets = {375 V2: [376 {377 id: {378 Concrete: {379 parents: 0,380 interior: 'Here',381 },382 },383 fun: {384 Fungible: TRANSFER_AMOUNT_RELAY,385 },386 },387 ],388 };389390 const feeAssetItem = 0;391392 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');393 });394395 await helper.wait.newBlocks(3);396397 balanceBobAfter = await helper.balance.getSubstrate(bob.address);398 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});399400 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;401 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;402 console.log(403 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',404 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),405 );406 console.log(407 '[Relay (Westend) -> Unique] transaction fees: %s WND',408 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),409 );410 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);411 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;412 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;413 });414415 itSub('Should connect and send Relay token back', async ({helper}) => {416 let relayTokenBalanceBefore: bigint;417 let relayTokenBalanceAfter: bigint;418 await usingRelayPlaygrounds(relayUrl, async (helper) => {419 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);420 });421422 const destination = {423 V2: {424 parents: 1,425 interior: {426 X1:{427 AccountId32: {428 network: 'Any',429 id: bob.addressRaw,430 },431 },432 },433 },434 };435436 const currencies: any = [437 [438 {439 NativeAssetId: 'Parent',440 },441 TRANSFER_AMOUNT_RELAY,442 ],443 ];444445 const feeItem = 0;446447 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');448449 balanceBobFinal = await helper.balance.getSubstrate(bob.address);450 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));451452 await usingRelayPlaygrounds(relayUrl, async (helper) => {453 await helper.wait.newBlocks(10);454 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);455456 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;457 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));458 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;459 });460 });461});462463describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {464 let alice: IKeyringPair;465 let randomAccount: IKeyringPair;466467 let balanceUniqueTokenInit: bigint;468 let balanceUniqueTokenMiddle: bigint;469 let balanceUniqueTokenFinal: bigint;470 let balanceAcalaTokenInit: bigint;471 let balanceAcalaTokenMiddle: bigint;472 let balanceAcalaTokenFinal: bigint;473 let balanceUniqueForeignTokenInit: bigint;474 let balanceUniqueForeignTokenMiddle: bigint;475 let balanceUniqueForeignTokenFinal: bigint;476477 // computed by a test transfer from prod Unique to prod Acala.478 // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48479 // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)480 const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;481 const acalaEps = 8n * 10n ** 16n;482483 let acalaBackwardTransferAmount: bigint;484485 before(async () => {486 await usingPlaygrounds(async (helper, privateKey) => {487 alice = await privateKey('//Alice');488 [randomAccount] = await helper.arrange.createAccounts([0n], alice);489490 // Set the default version to wrap the first message to other chains.491 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);492 });493494 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {495 const destination = {496 V2: {497 parents: 1,498 interior: {499 X1: {500 Parachain: UNIQUE_CHAIN,501 },502 },503 },504 };505506 const metadata = {507 name: 'Unique Network',508 symbol: 'UNQ',509 decimals: 18,510 minimalBalance: 1250000000000000000n,511 };512513 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);514 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);515 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);516 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});517 });518519 await usingPlaygrounds(async (helper) => {520 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);521 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);522 });523 });524525 itSub('Should connect and send UNQ to Acala', async ({helper}) => {526527 const destination = {528 V2: {529 parents: 1,530 interior: {531 X1: {532 Parachain: ACALA_CHAIN,533 },534 },535 },536 };537538 const beneficiary = {539 V2: {540 parents: 0,541 interior: {542 X1: {543 AccountId32: {544 network: 'Any',545 id: randomAccount.addressRaw,546 },547 },548 },549 },550 };551552 const assets = {553 V2: [554 {555 id: {556 Concrete: {557 parents: 0,558 interior: 'Here',559 },560 },561 fun: {562 Fungible: TRANSFER_AMOUNT,563 },564 },565 ],566 };567568 const feeAssetItem = 0;569570 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');571 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);572573 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;574 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));575 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;576577 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {578 await helper.wait.newBlocks(3);579580 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});581 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);582583 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;584 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;585 acalaBackwardTransferAmount = unqIncomeTransfer;586587 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;588589 console.log(590 '[Unique -> Acala] transaction fees on Acala: %s ACA',591 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),592 );593 console.log(594 '[Unique -> Acala] transaction fees on Acala: %s UNQ',595 helper.util.bigIntToDecimals(acaUnqFees),596 );597 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));598 expect(acaFees == 0n).to.be.true;599600 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;601602 expect(603 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,604 'Acala took different income fee, check the Acala foreign asset config',605 ).to.be.true;606 });607 });608609 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {610 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {611 const destination = {612 V2: {613 parents: 1,614 interior: {615 X2: [616 {Parachain: UNIQUE_CHAIN},617 {618 AccountId32: {619 network: 'Any',620 id: randomAccount.addressRaw,621 },622 },623 ],624 },625 },626 };627628 const id = {629 ForeignAsset: 0,630 };631632 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');633 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);634 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);635636 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;637 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;638639 console.log(640 '[Acala -> Unique] transaction fees on Acala: %s ACA',641 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),642 );643 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));644645 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;646 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;647 });648649 await helper.wait.newBlocks(3);650651 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);652 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;653 expect(actuallyDelivered > 0).to.be.true;654655 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));656657 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;658 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));659 expect(unqFees == 0n).to.be.true;660 });661662 itSub('Acala can send only up to its balance', async ({helper}) => {663 // set Acala's sovereign account's balance664 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);665 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);666 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);667668 const moreThanAcalaHas = acalaBalance * 2n;669670 let targetAccountBalance = 0n;671 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);672673 const uniqueMultilocation = {674 V2: {675 parents: 1,676 interior: {677 X1: {Parachain: UNIQUE_CHAIN},678 },679 },680 };681682 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(683 targetAccount.addressRaw,684 {685 Concrete: {686 parents: 0,687 interior: 'Here',688 },689 },690 moreThanAcalaHas,691 );692693 let maliciousXcmProgramSent: any;694 const maxWaitBlocks = 3;695696 // Try to trick Unique697 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {698 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);699700 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);701 });702703 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash704 && event.outcome.isFailedToTransactAsset);705706 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);707 expect(targetAccountBalance).to.be.equal(0n);708709 // But Acala still can send the correct amount710 const validTransferAmount = acalaBalance / 2n;711 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(712 targetAccount.addressRaw,713 {714 Concrete: {715 parents: 0,716 interior: 'Here',717 },718 },719 validTransferAmount,720 );721722 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {723 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);724 });725726 await helper.wait.newBlocks(maxWaitBlocks);727728 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);729 expect(targetAccountBalance).to.be.equal(validTransferAmount);730 });731732 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {733 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);734 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);735736 const uniqueMultilocation = {737 V2: {738 parents: 1,739 interior: {740 X1: {741 Parachain: UNIQUE_CHAIN,742 },743 },744 },745 };746747 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(748 targetAccount.addressRaw,749 {750 Concrete: {751 parents: 1,752 interior: {753 X1: {754 Parachain: UNIQUE_CHAIN,755 },756 },757 },758 },759 testAmount,760 );761762 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(763 targetAccount.addressRaw,764 {765 Concrete: {766 parents: 0,767 interior: 'Here',768 },769 },770 testAmount,771 );772773 let maliciousXcmProgramFullIdSent: any;774 let maliciousXcmProgramHereIdSent: any;775 const maxWaitBlocks = 3;776777 // Try to trick Unique using full UNQ identification778 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {779 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);780781 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);782 });783784 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash785 && event.outcome.isUntrustedReserveLocation);786787 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);788 expect(accountBalance).to.be.equal(0n);789790 // Try to trick Unique using shortened UNQ identification791 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {792 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);793794 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);795 });796797 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash798 && event.outcome.isUntrustedReserveLocation);799800 accountBalance = await helper.balance.getSubstrate(targetAccount.address);801 expect(accountBalance).to.be.equal(0n);802 });803});804805describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {806 let alice: IKeyringPair;807 let randomAccount: IKeyringPair;808 let unqFees: bigint;809 let balanceUniqueTokenInit: bigint;810 let balanceUniqueTokenMiddle: bigint;811 let balanceUniqueTokenFinal: bigint;812 const maxWaitBlocks = 6;813814 const uniqueAssetId = {815 Concrete: {816 parents: 1,817 interior: {818 X1: {819 Parachain: UNIQUE_CHAIN,820 },821 },822 },823 };824825 before(async () => {826 await usingPlaygrounds(async (helper, privateKey) => {827 alice = await privateKey('//Alice');828 [randomAccount] = await helper.arrange.createAccounts([0n], alice);829830 // Set the default version to wrap the first message to other chains.831 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);832 });833834 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {835 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])).toJSON() as [])836 .map(nToBigInt).length != 0;837838 if(!isWhitelisted) {839 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);840 }841842 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);843 });844845 await usingPlaygrounds(async (helper) => {846 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);847 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);848 });849 });850851 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {852853 const destination = {854 V2: {855 parents: 1,856 interior: {857 X1: {858 Parachain: POLKADEX_CHAIN,859 },860 },861 },862 };863864 const beneficiary = {865 V2: {866 parents: 0,867 interior: {868 X1: {869 AccountId32: {870 network: 'Any',871 id: randomAccount.addressRaw,872 },873 },874 },875 },876 };877878 const assets = {879 V2: [880 {881 id: {882 Concrete: {883 parents: 0,884 interior: 'Here',885 },886 },887 fun: {888 Fungible: TRANSFER_AMOUNT,889 },890 },891 ],892 };893894 const feeAssetItem = 0;895896 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');897 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);898 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);899900 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;901 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));902 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;903904 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {905 /*906 Since only the parachain part of the Polkadex907 infrastructure is launched (without their908 solochain validators), processing incoming909 assets will lead to an error.910 This error indicates that the Polkadex chain911 received a message from the Unique network,912 since the hash is being checked to ensure913 it matches what was sent.914 */915 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);916 });917 });918919920 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {921922 const uniqueMultilocation = {923 V2: {924 parents: 1,925 interior: {926 X1: {Parachain: UNIQUE_CHAIN},927 },928 },929 };930931 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(932 randomAccount.addressRaw,933 {934 Concrete: {935 parents: 1,936 interior: {937 X1: {Parachain: UNIQUE_CHAIN},938 },939 },940 },941 TRANSFER_AMOUNT,942 );943944 let xcmProgramSent: any;945946947 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {948 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);949950 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);951 });952953 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);954955 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);956957 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);958 });959960 itSub('Polkadex can send only up to its balance', async ({helper}) => {961 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);962 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);963 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);964 const moreThanPolkadexHas = 2n * polkadexBalance;965966 const targetAccount = helper.arrange.createEmptyAccount();967968 const uniqueMultilocation = {969 V2: {970 parents: 1,971 interior: {972 X1: {Parachain: UNIQUE_CHAIN},973 },974 },975 };976977 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(978 targetAccount.addressRaw,979 {980 Concrete: {981 parents: 0,982 interior: 'Here',983 },984 },985 moreThanPolkadexHas,986 );987988 let maliciousXcmProgramSent: any;989990991 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {992 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);993994 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);995 });996997 await expectFailedToTransact(helper, maliciousXcmProgramSent);998999 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1000 expect(targetAccountBalance).to.be.equal(0n);1001 });10021003 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {1004 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1005 const targetAccount = helper.arrange.createEmptyAccount();10061007 const uniqueMultilocation = {1008 V2: {1009 parents: 1,1010 interior: {1011 X1: {1012 Parachain: UNIQUE_CHAIN,1013 },1014 },1015 },1016 };10171018 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1019 targetAccount.addressRaw,1020 {1021 Concrete: {1022 parents: 1,1023 interior: {1024 X1: {1025 Parachain: UNIQUE_CHAIN,1026 },1027 },1028 },1029 },1030 testAmount,1031 );10321033 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1034 targetAccount.addressRaw,1035 {1036 Concrete: {1037 parents: 0,1038 interior: 'Here',1039 },1040 },1041 testAmount,1042 );10431044 let maliciousXcmProgramFullIdSent: any;1045 let maliciousXcmProgramHereIdSent: any;1046 const maxWaitBlocks = 3;10471048 // Try to trick Unique using full UNQ identification1049 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1050 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10511052 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1053 });10541055 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10561057 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1058 expect(accountBalance).to.be.equal(0n);10591060 // Try to trick Unique using shortened UNQ identification1061 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1062 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10631064 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1065 });10661067 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10681069 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1070 expect(accountBalance).to.be.equal(0n);1071 });1072});10731074// These tests are relevant only when1075// the the corresponding foreign assets are not registered1076describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1077 let alice: IKeyringPair;1078 let alith: IKeyringPair;10791080 const testAmount = 100_000_000_000n;1081 let uniqueParachainJunction;1082 let uniqueAccountJunction;10831084 let uniqueParachainMultilocation: any;1085 let uniqueAccountMultilocation: any;1086 let uniqueCombinedMultilocation: any;1087 let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V210881089 let messageSent: any;10901091 const maxWaitBlocks = 3;10921093 before(async () => {1094 await usingPlaygrounds(async (helper, privateKey) => {1095 alice = await privateKey('//Alice');10961097 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1098 uniqueAccountJunction = {1099 AccountId32: {1100 network: 'Any',1101 id: alice.addressRaw,1102 },1103 };11041105 uniqueParachainMultilocation = {1106 V2: {1107 parents: 1,1108 interior: {1109 X1: uniqueParachainJunction,1110 },1111 },1112 };11131114 uniqueAccountMultilocation = {1115 V2: {1116 parents: 0,1117 interior: {1118 X1: uniqueAccountJunction,1119 },1120 },1121 };11221123 uniqueCombinedMultilocation = {1124 V2: {1125 parents: 1,1126 interior: {1127 X2: [uniqueParachainJunction, uniqueAccountJunction],1128 },1129 },1130 };11311132 uniqueCombinedMultilocationAcala = {1133 V2: {1134 parents: 1,1135 interior: {1136 X2: [uniqueParachainJunction, uniqueAccountJunction],1137 },1138 },1139 };11401141 // Set the default version to wrap the first message to other chains.1142 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1143 });11441145 // eslint-disable-next-line require-await1146 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1147 alith = helper.account.alithAccount();1148 });1149 });1150115111521153 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1154 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1155 const id = {1156 Token: 'ACA',1157 };1158 const destination = uniqueCombinedMultilocationAcala;1159 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11601161 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1162 });11631164 await expectFailedToTransact(helper, messageSent);1165 });11661167 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1168 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1169 const id = 'SelfReserve';1170 const destination = uniqueCombinedMultilocation;1171 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11721173 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1174 });11751176 await expectFailedToTransact(helper, messageSent);1177 });11781179 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1180 await usingAstarPlaygrounds(astarUrl, async (helper) => {1181 const destinationParachain = uniqueParachainMultilocation;1182 const beneficiary = uniqueAccountMultilocation;1183 const assets = {1184 V2: [{1185 id: {1186 Concrete: {1187 parents: 0,1188 interior: 'Here',1189 },1190 },1191 fun: {1192 Fungible: testAmount,1193 },1194 }],1195 };1196 const feeAssetItem = 0;11971198 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1199 destinationParachain,1200 beneficiary,1201 assets,1202 feeAssetItem,1203 ]);12041205 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1206 });12071208 await expectFailedToTransact(helper, messageSent);1209 });12101211 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12121213 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1214 helper.arrange.createEmptyAccount().addressRaw,1215 {1216 Concrete: {1217 parents: 1,1218 interior: {1219 X1: {1220 Parachain: POLKADEX_CHAIN,1221 },1222 },1223 },1224 },1225 testAmount,1226 );12271228 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1229 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1230 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1231 });12321233 await expectFailedToTransact(helper, messageSent);1234 });1235});12361237describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1238 // Unique constants1239 let alice: IKeyringPair;1240 let uniqueAssetLocation;12411242 let randomAccountUnique: IKeyringPair;1243 let randomAccountMoonbeam: IKeyringPair;12441245 // Moonbeam constants1246 let assetId: string;12471248 const uniqueAssetMetadata = {1249 name: 'xcUnique',1250 symbol: 'xcUNQ',1251 decimals: 18,1252 isFrozen: false,1253 minimalBalance: 1n,1254 };12551256 let balanceUniqueTokenInit: bigint;1257 let balanceUniqueTokenMiddle: bigint;1258 let balanceUniqueTokenFinal: bigint;1259 let balanceForeignUnqTokenInit: bigint;1260 let balanceForeignUnqTokenMiddle: bigint;1261 let balanceForeignUnqTokenFinal: bigint;1262 let balanceGlmrTokenInit: bigint;1263 let balanceGlmrTokenMiddle: bigint;1264 let balanceGlmrTokenFinal: bigint;12651266 before(async () => {1267 await usingPlaygrounds(async (helper, privateKey) => {1268 alice = await privateKey('//Alice');1269 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12701271 balanceForeignUnqTokenInit = 0n;12721273 // Set the default version to wrap the first message to other chains.1274 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1275 });12761277 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1278 const alithAccount = helper.account.alithAccount();1279 const baltatharAccount = helper.account.baltatharAccount();1280 const dorothyAccount = helper.account.dorothyAccount();12811282 randomAccountMoonbeam = helper.account.create();12831284 // >>> Sponsoring Dorothy >>>1285 console.log('Sponsoring Dorothy.......');1286 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1287 console.log('Sponsoring Dorothy.......DONE');1288 // <<< Sponsoring Dorothy <<<12891290 uniqueAssetLocation = {1291 XCM: {1292 parents: 1,1293 interior: {X1: {Parachain: UNIQUE_CHAIN}},1294 },1295 };1296 const existentialDeposit = 1n;1297 const isSufficient = true;1298 const unitsPerSecond = 1n;1299 const numAssetsWeightHint = 0;13001301 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1302 location: uniqueAssetLocation,1303 metadata: uniqueAssetMetadata,1304 existentialDeposit,1305 isSufficient,1306 unitsPerSecond,1307 numAssetsWeightHint,1308 });13091310 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13111312 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13131314 // >>> Acquire Unique AssetId Info on Moonbeam >>>1315 console.log('Acquire Unique AssetId Info on Moonbeam.......');13161317 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13181319 console.log('UNQ asset ID is %s', assetId);1320 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1321 // >>> Acquire Unique AssetId Info on Moonbeam >>>13221323 // >>> Sponsoring random Account >>>1324 console.log('Sponsoring random Account.......');1325 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1326 console.log('Sponsoring random Account.......DONE');1327 // <<< Sponsoring random Account <<<13281329 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1330 });13311332 await usingPlaygrounds(async (helper) => {1333 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1334 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1335 });1336 });13371338 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1339 const currencyId = {1340 NativeAssetId: 'Here',1341 };1342 const dest = {1343 V2: {1344 parents: 1,1345 interior: {1346 X2: [1347 {Parachain: MOONBEAM_CHAIN},1348 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1349 ],1350 },1351 },1352 };1353 const amount = TRANSFER_AMOUNT;13541355 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13561357 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1358 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13591360 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1361 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1362 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13631364 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1365 await helper.wait.newBlocks(3);13661367 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13681369 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1370 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1371 expect(glmrFees == 0n).to.be.true;13721373 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13741375 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1376 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1377 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1378 });1379 });13801381 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1382 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1383 const asset = {1384 V2: {1385 id: {1386 Concrete: {1387 parents: 1,1388 interior: {1389 X1: {Parachain: UNIQUE_CHAIN},1390 },1391 },1392 },1393 fun: {1394 Fungible: TRANSFER_AMOUNT,1395 },1396 },1397 };1398 const destination = {1399 V2: {1400 parents: 1,1401 interior: {1402 X2: [1403 {Parachain: UNIQUE_CHAIN},1404 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1405 ],1406 },1407 },1408 };14091410 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14111412 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14131414 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1415 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1416 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14171418 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14191420 expect(unqRandomAccountAsset).to.be.null;14211422 balanceForeignUnqTokenFinal = 0n;14231424 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1425 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1426 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1427 });14281429 await helper.wait.newBlocks(3);14301431 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1432 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1433 expect(actuallyDelivered > 0).to.be.true;14341435 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14361437 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1438 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1439 expect(unqFees == 0n).to.be.true;1440 });14411442 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1443 // set Moonbeam's sovereign account's balance1444 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1445 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1446 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14471448 const moreThanMoonbeamHas = moonbeamBalance * 2n;14491450 let targetAccountBalance = 0n;1451 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14521453 const uniqueMultilocation = {1454 V2: {1455 parents: 1,1456 interior: {1457 X1: {Parachain: UNIQUE_CHAIN},1458 },1459 },1460 };14611462 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1463 targetAccount.addressRaw,1464 {1465 Concrete: {1466 parents: 0,1467 interior: 'Here',1468 },1469 },1470 moreThanMoonbeamHas,1471 );14721473 let maliciousXcmProgramSent: any;1474 const maxWaitBlocks = 3;14751476 // Try to trick Unique1477 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1478 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14791480 // Needed to bypass the call filter.1481 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1482 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14831484 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1485 });14861487 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1488 && event.outcome.isFailedToTransactAsset);14891490 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1491 expect(targetAccountBalance).to.be.equal(0n);14921493 // But Moonbeam still can send the correct amount1494 const validTransferAmount = moonbeamBalance / 2n;1495 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1496 targetAccount.addressRaw,1497 {1498 Concrete: {1499 parents: 0,1500 interior: 'Here',1501 },1502 },1503 validTransferAmount,1504 );15051506 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1507 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);15081509 // Needed to bypass the call filter.1510 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1511 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1512 });15131514 await helper.wait.newBlocks(maxWaitBlocks);15151516 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1517 expect(targetAccountBalance).to.be.equal(validTransferAmount);1518 });15191520 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1521 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1522 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15231524 const uniqueMultilocation = {1525 V2: {1526 parents: 1,1527 interior: {1528 X1: {1529 Parachain: UNIQUE_CHAIN,1530 },1531 },1532 },1533 };15341535 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1536 targetAccount.addressRaw,1537 {1538 Concrete: {1539 parents: 0,1540 interior: {1541 X1: {1542 Parachain: UNIQUE_CHAIN,1543 },1544 },1545 },1546 },1547 testAmount,1548 );15491550 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1551 targetAccount.addressRaw,1552 {1553 Concrete: {1554 parents: 0,1555 interior: 'Here',1556 },1557 },1558 testAmount,1559 );15601561 let maliciousXcmProgramFullIdSent: any;1562 let maliciousXcmProgramHereIdSent: any;1563 const maxWaitBlocks = 3;15641565 // Try to trick Unique using full UNQ identification1566 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1567 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15681569 // Needed to bypass the call filter.1570 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1571 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15721573 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1574 });15751576 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1577 && event.outcome.isUntrustedReserveLocation);15781579 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1580 expect(accountBalance).to.be.equal(0n);15811582 // Try to trick Unique using shortened UNQ identification1583 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1584 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15851586 // Needed to bypass the call filter.1587 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1588 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15891590 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1591 });15921593 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1594 && event.outcome.isUntrustedReserveLocation);15951596 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1597 expect(accountBalance).to.be.equal(0n);1598 });1599});16001601describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1602 let alice: IKeyringPair;1603 let randomAccount: IKeyringPair;16041605 const UNQ_ASSET_ID_ON_ASTAR = 1;1606 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;16071608 // Unique -> Astar1609 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1610 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1611 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1612 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens16131614 // Astar -> Unique1615 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1616 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ16171618 let balanceAfterUniqueToAstarXCM: bigint;16191620 before(async () => {1621 await usingPlaygrounds(async (helper, privateKey) => {1622 alice = await privateKey('//Alice');1623 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1624 console.log('randomAccount', randomAccount.address);16251626 // Set the default version to wrap the first message to other chains.1627 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1628 });16291630 await usingAstarPlaygrounds(astarUrl, async (helper) => {1631 console.log('1. Create foreign asset and metadata');1632 // TODO update metadata with values from production1633 await helper.assets.create(1634 alice,1635 UNQ_ASSET_ID_ON_ASTAR,1636 alice.address,1637 UNQ_MINIMAL_BALANCE_ON_ASTAR,1638 );16391640 await helper.assets.setMetadata(1641 alice,1642 UNQ_ASSET_ID_ON_ASTAR,1643 'Cross chain UNQ',1644 'xcUNQ',1645 Number(UNQ_DECIMALS),1646 );16471648 console.log('2. Register asset location on Astar');1649 const assetLocation = {1650 V2: {1651 parents: 1,1652 interior: {1653 X1: {1654 Parachain: UNIQUE_CHAIN,1655 },1656 },1657 },1658 };16591660 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16611662 console.log('3. Set UNQ payment for XCM execution on Astar');1663 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);16641665 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1666 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1667 });1668 });16691670 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1671 const destination = {1672 V2: {1673 parents: 1,1674 interior: {1675 X1: {1676 Parachain: ASTAR_CHAIN,1677 },1678 },1679 },1680 };16811682 const beneficiary = {1683 V2: {1684 parents: 0,1685 interior: {1686 X1: {1687 AccountId32: {1688 network: 'Any',1689 id: randomAccount.addressRaw,1690 },1691 },1692 },1693 },1694 };16951696 const assets = {1697 V2: [1698 {1699 id: {1700 Concrete: {1701 parents: 0,1702 interior: 'Here',1703 },1704 },1705 fun: {1706 Fungible: unqToAstarTransferred,1707 },1708 },1709 ],1710 };17111712 // Initial balance is 100 UNQ1713 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1714 console.log(`Initial balance is: ${balanceBefore}`);17151716 const feeAssetItem = 0;1717 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');17181719 // Balance after reserve transfer is less than 901720 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1721 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1722 console.log(`Unique's UNQ commission is: ${balanceBefore-balanceAfterUniqueToAstarXCM}`);1723 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17241725 await usingAstarPlaygrounds(astarUrl, async (helper) => {1726 await helper.wait.newBlocks(3);1727 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1728 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17291730 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1731 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred-xcUNQbalance!}`);17321733 expect(xcUNQbalance).to.eq(unqToAstarArrived);1734 // Astar balance does not changed1735 expect(astarBalance).to.eq(astarInitialBalance);1736 });1737 });17381739 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1740 await usingAstarPlaygrounds(astarUrl, async (helper) => {1741 const destination = {1742 V2: {1743 parents: 1,1744 interior: {1745 X1: {1746 Parachain: UNIQUE_CHAIN,1747 },1748 },1749 },1750 };17511752 const beneficiary = {1753 V2: {1754 parents: 0,1755 interior: {1756 X1: {1757 AccountId32: {1758 network: 'Any',1759 id: randomAccount.addressRaw,1760 },1761 },1762 },1763 },1764 };17651766 const assets = {1767 V2: [1768 {1769 id: {1770 Concrete: {1771 parents: 1,1772 interior: {1773 X1: {1774 Parachain: UNIQUE_CHAIN,1775 },1776 },1777 },1778 },1779 fun: {1780 Fungible: unqFromAstarTransfered,1781 },1782 },1783 ],1784 };17851786 // Initial balance is 1 ASTR1787 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1788 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1789 expect(balanceASTRbefore).to.eq(astarInitialBalance);17901791 const feeAssetItem = 0;1792 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1793 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17941795 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1796 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1797 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17981799 // Assert: xcUNQ balance correctly decreased1800 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1801 // Assert: ASTR balance is 0.996...1802 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1803 });18041805 await helper.wait.newBlocks(3);1806 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1807 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1808 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1809 });18101811 itSub('Astar can send only up to its balance', async ({helper}) => {1812 // set Astar's sovereign account's balance1813 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1814 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1815 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);18161817 const moreThanAstarHas = astarBalance * 2n;18181819 let targetAccountBalance = 0n;1820 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18211822 const uniqueMultilocation = {1823 V2: {1824 parents: 1,1825 interior: {1826 X1: {Parachain: UNIQUE_CHAIN},1827 },1828 },1829 };18301831 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1832 targetAccount.addressRaw,1833 {1834 Concrete: {1835 parents: 0,1836 interior: 'Here',1837 },1838 },1839 moreThanAstarHas,1840 );18411842 let maliciousXcmProgramSent: any;1843 const maxWaitBlocks = 3;18441845 // Try to trick Unique1846 await usingAstarPlaygrounds(astarUrl, async (helper) => {1847 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18481849 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1850 });18511852 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1853 && event.outcome.isFailedToTransactAsset);18541855 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1856 expect(targetAccountBalance).to.be.equal(0n);18571858 // But Astar still can send the correct amount1859 const validTransferAmount = astarBalance / 2n;1860 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1861 targetAccount.addressRaw,1862 {1863 Concrete: {1864 parents: 0,1865 interior: 'Here',1866 },1867 },1868 validTransferAmount,1869 );18701871 await usingAstarPlaygrounds(astarUrl, async (helper) => {1872 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1873 });18741875 await helper.wait.newBlocks(maxWaitBlocks);18761877 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1878 expect(targetAccountBalance).to.be.equal(validTransferAmount);1879 });18801881 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1882 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1883 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18841885 const uniqueMultilocation = {1886 V2: {1887 parents: 1,1888 interior: {1889 X1: {1890 Parachain: UNIQUE_CHAIN,1891 },1892 },1893 },1894 };18951896 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1897 targetAccount.addressRaw,1898 {1899 Concrete: {1900 parents: 1,1901 interior: {1902 X1: {1903 Parachain: UNIQUE_CHAIN,1904 },1905 },1906 },1907 },1908 testAmount,1909 );19101911 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1912 targetAccount.addressRaw,1913 {1914 Concrete: {1915 parents: 0,1916 interior: 'Here',1917 },1918 },1919 testAmount,1920 );19211922 let maliciousXcmProgramFullIdSent: any;1923 let maliciousXcmProgramHereIdSent: any;1924 const maxWaitBlocks = 3;19251926 // Try to trick Unique using full UNQ identification1927 await usingAstarPlaygrounds(astarUrl, async (helper) => {1928 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);19291930 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1931 });19321933 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1934 && event.outcome.isUntrustedReserveLocation);19351936 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1937 expect(accountBalance).to.be.equal(0n);19381939 // Try to trick Unique using shortened UNQ identification1940 await usingAstarPlaygrounds(astarUrl, async (helper) => {1941 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19421943 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1944 });19451946 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1947 && event.outcome.isUntrustedReserveLocation);19481949 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1950 expect(accountBalance).to.be.equal(0n);1951 });1952});vendor/baedeker-librarydiffbeforeafterbothbinary blob — no preview