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;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});