git.delta.rocks / unique-network / refs/commits / 9d3aabb4c1a9

difftreelog

test(polkadex) added reserve malicious test

PraetorP2023-09-11parent: #683a128.patch.diff
in: master

2 files changed

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

binary blob — no preview