difftreelog
doc(polkadex CI): added comment
in: master
1 file 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;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;837 /*838 Check whether the Unique token has been added839 to the whitelist, since an error will occur840 if it is added again.841 */842 if(!isWhitelisted) {843 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);844 }845846 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);847 });848849 await usingPlaygrounds(async (helper) => {850 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);851 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);852 });853 });854855 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {856857 const destination = {858 V2: {859 parents: 1,860 interior: {861 X1: {862 Parachain: POLKADEX_CHAIN,863 },864 },865 },866 };867868 const beneficiary = {869 V2: {870 parents: 0,871 interior: {872 X1: {873 AccountId32: {874 network: 'Any',875 id: randomAccount.addressRaw,876 },877 },878 },879 },880 };881882 const assets = {883 V2: [884 {885 id: {886 Concrete: {887 parents: 0,888 interior: 'Here',889 },890 },891 fun: {892 Fungible: TRANSFER_AMOUNT,893 },894 },895 ],896 };897898 const feeAssetItem = 0;899900 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');901 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);902 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);903904 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;905 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));906 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;907908 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {909 /*910 Since only the parachain part of the Polkadex911 infrastructure is launched (without their912 solochain validators), processing incoming913 assets will lead to an error.914 This error indicates that the Polkadex chain915 received a message from the Unique network,916 since the hash is being checked to ensure917 it matches what was sent.918 */919 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);920 });921 });922923924 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {925926 const uniqueMultilocation = {927 V2: {928 parents: 1,929 interior: {930 X1: {Parachain: UNIQUE_CHAIN},931 },932 },933 };934935 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(936 randomAccount.addressRaw,937 {938 Concrete: {939 parents: 1,940 interior: {941 X1: {Parachain: UNIQUE_CHAIN},942 },943 },944 },945 TRANSFER_AMOUNT,946 );947948 let xcmProgramSent: any;949950951 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {952 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);953954 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);955 });956957 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);958959 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);960961 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);962 });963964 itSub('Polkadex can send only up to its balance', async ({helper}) => {965 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);966 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);967 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);968 const moreThanPolkadexHas = 2n * polkadexBalance;969970 const targetAccount = helper.arrange.createEmptyAccount();971972 const uniqueMultilocation = {973 V2: {974 parents: 1,975 interior: {976 X1: {Parachain: UNIQUE_CHAIN},977 },978 },979 };980981 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(982 targetAccount.addressRaw,983 {984 Concrete: {985 parents: 0,986 interior: 'Here',987 },988 },989 moreThanPolkadexHas,990 );991992 let maliciousXcmProgramSent: any;993994995 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {996 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);997998 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);999 });10001001 await expectFailedToTransact(helper, maliciousXcmProgramSent);10021003 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1004 expect(targetAccountBalance).to.be.equal(0n);1005 });10061007 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {1008 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1009 const targetAccount = helper.arrange.createEmptyAccount();10101011 const uniqueMultilocation = {1012 V2: {1013 parents: 1,1014 interior: {1015 X1: {1016 Parachain: UNIQUE_CHAIN,1017 },1018 },1019 },1020 };10211022 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1023 targetAccount.addressRaw,1024 {1025 Concrete: {1026 parents: 1,1027 interior: {1028 X1: {1029 Parachain: UNIQUE_CHAIN,1030 },1031 },1032 },1033 },1034 testAmount,1035 );10361037 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1038 targetAccount.addressRaw,1039 {1040 Concrete: {1041 parents: 0,1042 interior: 'Here',1043 },1044 },1045 testAmount,1046 );10471048 let maliciousXcmProgramFullIdSent: any;1049 let maliciousXcmProgramHereIdSent: any;1050 const maxWaitBlocks = 3;10511052 // Try to trick Unique using full UNQ identification1053 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1054 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10551056 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1057 });10581059 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10601061 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1062 expect(accountBalance).to.be.equal(0n);10631064 // Try to trick Unique using shortened UNQ identification1065 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1066 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10671068 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1069 });10701071 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10721073 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1074 expect(accountBalance).to.be.equal(0n);1075 });1076});10771078// These tests are relevant only when1079// the the corresponding foreign assets are not registered1080describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1081 let alice: IKeyringPair;1082 let alith: IKeyringPair;10831084 const testAmount = 100_000_000_000n;1085 let uniqueParachainJunction;1086 let uniqueAccountJunction;10871088 let uniqueParachainMultilocation: any;1089 let uniqueAccountMultilocation: any;1090 let uniqueCombinedMultilocation: any;1091 let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V210921093 let messageSent: any;10941095 const maxWaitBlocks = 3;10961097 before(async () => {1098 await usingPlaygrounds(async (helper, privateKey) => {1099 alice = await privateKey('//Alice');11001101 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1102 uniqueAccountJunction = {1103 AccountId32: {1104 network: 'Any',1105 id: alice.addressRaw,1106 },1107 };11081109 uniqueParachainMultilocation = {1110 V2: {1111 parents: 1,1112 interior: {1113 X1: uniqueParachainJunction,1114 },1115 },1116 };11171118 uniqueAccountMultilocation = {1119 V2: {1120 parents: 0,1121 interior: {1122 X1: uniqueAccountJunction,1123 },1124 },1125 };11261127 uniqueCombinedMultilocation = {1128 V2: {1129 parents: 1,1130 interior: {1131 X2: [uniqueParachainJunction, uniqueAccountJunction],1132 },1133 },1134 };11351136 uniqueCombinedMultilocationAcala = {1137 V2: {1138 parents: 1,1139 interior: {1140 X2: [uniqueParachainJunction, uniqueAccountJunction],1141 },1142 },1143 };11441145 // Set the default version to wrap the first message to other chains.1146 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1147 });11481149 // eslint-disable-next-line require-await1150 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1151 alith = helper.account.alithAccount();1152 });1153 });1154115511561157 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1158 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1159 const id = {1160 Token: 'ACA',1161 };1162 const destination = uniqueCombinedMultilocationAcala;1163 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11641165 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1166 });11671168 await expectFailedToTransact(helper, messageSent);1169 });11701171 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1172 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1173 const id = 'SelfReserve';1174 const destination = uniqueCombinedMultilocation;1175 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11761177 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1178 });11791180 await expectFailedToTransact(helper, messageSent);1181 });11821183 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1184 await usingAstarPlaygrounds(astarUrl, async (helper) => {1185 const destinationParachain = uniqueParachainMultilocation;1186 const beneficiary = uniqueAccountMultilocation;1187 const assets = {1188 V2: [{1189 id: {1190 Concrete: {1191 parents: 0,1192 interior: 'Here',1193 },1194 },1195 fun: {1196 Fungible: testAmount,1197 },1198 }],1199 };1200 const feeAssetItem = 0;12011202 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1203 destinationParachain,1204 beneficiary,1205 assets,1206 feeAssetItem,1207 ]);12081209 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1210 });12111212 await expectFailedToTransact(helper, messageSent);1213 });12141215 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12161217 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1218 helper.arrange.createEmptyAccount().addressRaw,1219 {1220 Concrete: {1221 parents: 1,1222 interior: {1223 X1: {1224 Parachain: POLKADEX_CHAIN,1225 },1226 },1227 },1228 },1229 testAmount,1230 );12311232 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1233 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1234 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1235 });12361237 await expectFailedToTransact(helper, messageSent);1238 });1239});12401241describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1242 // Unique constants1243 let alice: IKeyringPair;1244 let uniqueAssetLocation;12451246 let randomAccountUnique: IKeyringPair;1247 let randomAccountMoonbeam: IKeyringPair;12481249 // Moonbeam constants1250 let assetId: string;12511252 const uniqueAssetMetadata = {1253 name: 'xcUnique',1254 symbol: 'xcUNQ',1255 decimals: 18,1256 isFrozen: false,1257 minimalBalance: 1n,1258 };12591260 let balanceUniqueTokenInit: bigint;1261 let balanceUniqueTokenMiddle: bigint;1262 let balanceUniqueTokenFinal: bigint;1263 let balanceForeignUnqTokenInit: bigint;1264 let balanceForeignUnqTokenMiddle: bigint;1265 let balanceForeignUnqTokenFinal: bigint;1266 let balanceGlmrTokenInit: bigint;1267 let balanceGlmrTokenMiddle: bigint;1268 let balanceGlmrTokenFinal: bigint;12691270 before(async () => {1271 await usingPlaygrounds(async (helper, privateKey) => {1272 alice = await privateKey('//Alice');1273 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12741275 balanceForeignUnqTokenInit = 0n;12761277 // Set the default version to wrap the first message to other chains.1278 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1279 });12801281 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1282 const alithAccount = helper.account.alithAccount();1283 const baltatharAccount = helper.account.baltatharAccount();1284 const dorothyAccount = helper.account.dorothyAccount();12851286 randomAccountMoonbeam = helper.account.create();12871288 // >>> Sponsoring Dorothy >>>1289 console.log('Sponsoring Dorothy.......');1290 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1291 console.log('Sponsoring Dorothy.......DONE');1292 // <<< Sponsoring Dorothy <<<12931294 uniqueAssetLocation = {1295 XCM: {1296 parents: 1,1297 interior: {X1: {Parachain: UNIQUE_CHAIN}},1298 },1299 };1300 const existentialDeposit = 1n;1301 const isSufficient = true;1302 const unitsPerSecond = 1n;1303 const numAssetsWeightHint = 0;13041305 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1306 location: uniqueAssetLocation,1307 metadata: uniqueAssetMetadata,1308 existentialDeposit,1309 isSufficient,1310 unitsPerSecond,1311 numAssetsWeightHint,1312 });13131314 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13151316 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13171318 // >>> Acquire Unique AssetId Info on Moonbeam >>>1319 console.log('Acquire Unique AssetId Info on Moonbeam.......');13201321 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13221323 console.log('UNQ asset ID is %s', assetId);1324 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1325 // >>> Acquire Unique AssetId Info on Moonbeam >>>13261327 // >>> Sponsoring random Account >>>1328 console.log('Sponsoring random Account.......');1329 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1330 console.log('Sponsoring random Account.......DONE');1331 // <<< Sponsoring random Account <<<13321333 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1334 });13351336 await usingPlaygrounds(async (helper) => {1337 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1338 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1339 });1340 });13411342 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1343 const currencyId = {1344 NativeAssetId: 'Here',1345 };1346 const dest = {1347 V2: {1348 parents: 1,1349 interior: {1350 X2: [1351 {Parachain: MOONBEAM_CHAIN},1352 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1353 ],1354 },1355 },1356 };1357 const amount = TRANSFER_AMOUNT;13581359 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13601361 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1362 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13631364 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1365 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1366 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13671368 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1369 await helper.wait.newBlocks(3);13701371 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13721373 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1374 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1375 expect(glmrFees == 0n).to.be.true;13761377 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13781379 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1380 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1381 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1382 });1383 });13841385 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1386 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1387 const asset = {1388 V2: {1389 id: {1390 Concrete: {1391 parents: 1,1392 interior: {1393 X1: {Parachain: UNIQUE_CHAIN},1394 },1395 },1396 },1397 fun: {1398 Fungible: TRANSFER_AMOUNT,1399 },1400 },1401 };1402 const destination = {1403 V2: {1404 parents: 1,1405 interior: {1406 X2: [1407 {Parachain: UNIQUE_CHAIN},1408 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1409 ],1410 },1411 },1412 };14131414 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14151416 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14171418 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1419 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1420 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14211422 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14231424 expect(unqRandomAccountAsset).to.be.null;14251426 balanceForeignUnqTokenFinal = 0n;14271428 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1429 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1430 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1431 });14321433 await helper.wait.newBlocks(3);14341435 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1436 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1437 expect(actuallyDelivered > 0).to.be.true;14381439 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14401441 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1442 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1443 expect(unqFees == 0n).to.be.true;1444 });14451446 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1447 // set Moonbeam's sovereign account's balance1448 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1449 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1450 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14511452 const moreThanMoonbeamHas = moonbeamBalance * 2n;14531454 let targetAccountBalance = 0n;1455 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14561457 const uniqueMultilocation = {1458 V2: {1459 parents: 1,1460 interior: {1461 X1: {Parachain: UNIQUE_CHAIN},1462 },1463 },1464 };14651466 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1467 targetAccount.addressRaw,1468 {1469 Concrete: {1470 parents: 0,1471 interior: 'Here',1472 },1473 },1474 moreThanMoonbeamHas,1475 );14761477 let maliciousXcmProgramSent: any;1478 const maxWaitBlocks = 3;14791480 // Try to trick Unique1481 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1482 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14831484 // Needed to bypass the call filter.1485 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1486 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14871488 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1489 });14901491 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1492 && event.outcome.isFailedToTransactAsset);14931494 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1495 expect(targetAccountBalance).to.be.equal(0n);14961497 // But Moonbeam still can send the correct amount1498 const validTransferAmount = moonbeamBalance / 2n;1499 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1500 targetAccount.addressRaw,1501 {1502 Concrete: {1503 parents: 0,1504 interior: 'Here',1505 },1506 },1507 validTransferAmount,1508 );15091510 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1511 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);15121513 // Needed to bypass the call filter.1514 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1515 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1516 });15171518 await helper.wait.newBlocks(maxWaitBlocks);15191520 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1521 expect(targetAccountBalance).to.be.equal(validTransferAmount);1522 });15231524 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1525 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1526 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15271528 const uniqueMultilocation = {1529 V2: {1530 parents: 1,1531 interior: {1532 X1: {1533 Parachain: UNIQUE_CHAIN,1534 },1535 },1536 },1537 };15381539 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1540 targetAccount.addressRaw,1541 {1542 Concrete: {1543 parents: 0,1544 interior: {1545 X1: {1546 Parachain: UNIQUE_CHAIN,1547 },1548 },1549 },1550 },1551 testAmount,1552 );15531554 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1555 targetAccount.addressRaw,1556 {1557 Concrete: {1558 parents: 0,1559 interior: 'Here',1560 },1561 },1562 testAmount,1563 );15641565 let maliciousXcmProgramFullIdSent: any;1566 let maliciousXcmProgramHereIdSent: any;1567 const maxWaitBlocks = 3;15681569 // Try to trick Unique using full UNQ identification1570 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1571 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15721573 // Needed to bypass the call filter.1574 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1575 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15761577 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1578 });15791580 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1581 && event.outcome.isUntrustedReserveLocation);15821583 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1584 expect(accountBalance).to.be.equal(0n);15851586 // Try to trick Unique using shortened UNQ identification1587 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1588 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15891590 // Needed to bypass the call filter.1591 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1592 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15931594 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1595 });15961597 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1598 && event.outcome.isUntrustedReserveLocation);15991600 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1601 expect(accountBalance).to.be.equal(0n);1602 });1603});16041605describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1606 let alice: IKeyringPair;1607 let randomAccount: IKeyringPair;16081609 const UNQ_ASSET_ID_ON_ASTAR = 1;1610 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;16111612 // Unique -> Astar1613 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1614 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1615 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1616 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens16171618 // Astar -> Unique1619 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1620 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ16211622 let balanceAfterUniqueToAstarXCM: bigint;16231624 before(async () => {1625 await usingPlaygrounds(async (helper, privateKey) => {1626 alice = await privateKey('//Alice');1627 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1628 console.log('randomAccount', randomAccount.address);16291630 // Set the default version to wrap the first message to other chains.1631 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1632 });16331634 await usingAstarPlaygrounds(astarUrl, async (helper) => {1635 console.log('1. Create foreign asset and metadata');1636 // TODO update metadata with values from production1637 await helper.assets.create(1638 alice,1639 UNQ_ASSET_ID_ON_ASTAR,1640 alice.address,1641 UNQ_MINIMAL_BALANCE_ON_ASTAR,1642 );16431644 await helper.assets.setMetadata(1645 alice,1646 UNQ_ASSET_ID_ON_ASTAR,1647 'Cross chain UNQ',1648 'xcUNQ',1649 Number(UNQ_DECIMALS),1650 );16511652 console.log('2. Register asset location on Astar');1653 const assetLocation = {1654 V2: {1655 parents: 1,1656 interior: {1657 X1: {1658 Parachain: UNIQUE_CHAIN,1659 },1660 },1661 },1662 };16631664 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16651666 console.log('3. Set UNQ payment for XCM execution on Astar');1667 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);16681669 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1670 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1671 });1672 });16731674 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1675 const destination = {1676 V2: {1677 parents: 1,1678 interior: {1679 X1: {1680 Parachain: ASTAR_CHAIN,1681 },1682 },1683 },1684 };16851686 const beneficiary = {1687 V2: {1688 parents: 0,1689 interior: {1690 X1: {1691 AccountId32: {1692 network: 'Any',1693 id: randomAccount.addressRaw,1694 },1695 },1696 },1697 },1698 };16991700 const assets = {1701 V2: [1702 {1703 id: {1704 Concrete: {1705 parents: 0,1706 interior: 'Here',1707 },1708 },1709 fun: {1710 Fungible: unqToAstarTransferred,1711 },1712 },1713 ],1714 };17151716 // Initial balance is 100 UNQ1717 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1718 console.log(`Initial balance is: ${balanceBefore}`);17191720 const feeAssetItem = 0;1721 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');17221723 // Balance after reserve transfer is less than 901724 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1725 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1726 console.log(`Unique's UNQ commission is: ${balanceBefore-balanceAfterUniqueToAstarXCM}`);1727 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17281729 await usingAstarPlaygrounds(astarUrl, async (helper) => {1730 await helper.wait.newBlocks(3);1731 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1732 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17331734 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1735 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred-xcUNQbalance!}`);17361737 expect(xcUNQbalance).to.eq(unqToAstarArrived);1738 // Astar balance does not changed1739 expect(astarBalance).to.eq(astarInitialBalance);1740 });1741 });17421743 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1744 await usingAstarPlaygrounds(astarUrl, async (helper) => {1745 const destination = {1746 V2: {1747 parents: 1,1748 interior: {1749 X1: {1750 Parachain: UNIQUE_CHAIN,1751 },1752 },1753 },1754 };17551756 const beneficiary = {1757 V2: {1758 parents: 0,1759 interior: {1760 X1: {1761 AccountId32: {1762 network: 'Any',1763 id: randomAccount.addressRaw,1764 },1765 },1766 },1767 },1768 };17691770 const assets = {1771 V2: [1772 {1773 id: {1774 Concrete: {1775 parents: 1,1776 interior: {1777 X1: {1778 Parachain: UNIQUE_CHAIN,1779 },1780 },1781 },1782 },1783 fun: {1784 Fungible: unqFromAstarTransfered,1785 },1786 },1787 ],1788 };17891790 // Initial balance is 1 ASTR1791 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1792 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1793 expect(balanceASTRbefore).to.eq(astarInitialBalance);17941795 const feeAssetItem = 0;1796 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1797 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17981799 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1800 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1801 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);18021803 // Assert: xcUNQ balance correctly decreased1804 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1805 // Assert: ASTR balance is 0.996...1806 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1807 });18081809 await helper.wait.newBlocks(3);1810 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1811 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1812 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1813 });18141815 itSub('Astar can send only up to its balance', async ({helper}) => {1816 // set Astar's sovereign account's balance1817 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1818 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1819 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);18201821 const moreThanAstarHas = astarBalance * 2n;18221823 let targetAccountBalance = 0n;1824 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18251826 const uniqueMultilocation = {1827 V2: {1828 parents: 1,1829 interior: {1830 X1: {Parachain: UNIQUE_CHAIN},1831 },1832 },1833 };18341835 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1836 targetAccount.addressRaw,1837 {1838 Concrete: {1839 parents: 0,1840 interior: 'Here',1841 },1842 },1843 moreThanAstarHas,1844 );18451846 let maliciousXcmProgramSent: any;1847 const maxWaitBlocks = 3;18481849 // Try to trick Unique1850 await usingAstarPlaygrounds(astarUrl, async (helper) => {1851 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18521853 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1854 });18551856 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1857 && event.outcome.isFailedToTransactAsset);18581859 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1860 expect(targetAccountBalance).to.be.equal(0n);18611862 // But Astar still can send the correct amount1863 const validTransferAmount = astarBalance / 2n;1864 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1865 targetAccount.addressRaw,1866 {1867 Concrete: {1868 parents: 0,1869 interior: 'Here',1870 },1871 },1872 validTransferAmount,1873 );18741875 await usingAstarPlaygrounds(astarUrl, async (helper) => {1876 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1877 });18781879 await helper.wait.newBlocks(maxWaitBlocks);18801881 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1882 expect(targetAccountBalance).to.be.equal(validTransferAmount);1883 });18841885 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1886 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1887 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18881889 const uniqueMultilocation = {1890 V2: {1891 parents: 1,1892 interior: {1893 X1: {1894 Parachain: UNIQUE_CHAIN,1895 },1896 },1897 },1898 };18991900 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1901 targetAccount.addressRaw,1902 {1903 Concrete: {1904 parents: 1,1905 interior: {1906 X1: {1907 Parachain: UNIQUE_CHAIN,1908 },1909 },1910 },1911 },1912 testAmount,1913 );19141915 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1916 targetAccount.addressRaw,1917 {1918 Concrete: {1919 parents: 0,1920 interior: 'Here',1921 },1922 },1923 testAmount,1924 );19251926 let maliciousXcmProgramFullIdSent: any;1927 let maliciousXcmProgramHereIdSent: any;1928 const maxWaitBlocks = 3;19291930 // Try to trick Unique using full UNQ identification1931 await usingAstarPlaygrounds(astarUrl, async (helper) => {1932 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);19331934 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1935 });19361937 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1938 && event.outcome.isUntrustedReserveLocation);19391940 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1941 expect(accountBalance).to.be.equal(0n);19421943 // Try to trick Unique using shortened UNQ identification1944 await usingAstarPlaygrounds(astarUrl, async (helper) => {1945 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19461947 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1948 });19491950 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1951 && event.outcome.isUntrustedReserveLocation);19521953 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1954 expect(accountBalance).to.be.equal(0n);1955 });1956});