git.delta.rocks / unique-network / refs/commits / 369a51ded768

difftreelog

source

tests/src/xcm/xcmUnique.test.ts54.0 KiBsourcehistory
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} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';2122const UNIQUE_CHAIN = 2037;23const STATEMINT_CHAIN = 1000;24const ACALA_CHAIN = 2000;25const MOONBEAM_CHAIN = 2004;26const ASTAR_CHAIN = 2006;2728const STATEMINT_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemintUrl = config.statemintUrl;32const acalaUrl = config.acalaUrl;33const moonbeamUrl = config.moonbeamUrl;34const astarUrl = config.astarUrl;3536const RELAY_DECIMALS = 12;37const STATEMINT_DECIMALS = 12;38const ACALA_DECIMALS = 12;39const ASTAR_DECIMALS = 18n;40const UNQ_DECIMALS = 18n;4142const TRANSFER_AMOUNT = 2000000000000000000000000n;4344const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4546const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4748const USDT_ASSET_ID = 100;49const USDT_ASSET_METADATA_DECIMALS = 18;50const USDT_ASSET_METADATA_NAME = 'USDT';51const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';52const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;53const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5455const SAFE_XCM_VERSION = 2;5657describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {58  let alice: IKeyringPair;59  let bob: IKeyringPair;6061  let balanceStmnBefore: bigint;62  let balanceStmnAfter: bigint;6364  let balanceUniqueBefore: bigint;65  let balanceUniqueAfter: bigint;66  let balanceUniqueFinal: bigint;6768  let balanceBobBefore: bigint;69  let balanceBobAfter: bigint;70  let balanceBobFinal: bigint;7172  let balanceBobRelayTokenBefore: bigint;73  let balanceBobRelayTokenAfter: bigint;747576  before(async () => {77    await usingPlaygrounds(async (helper, privateKey) => {78      alice = await privateKey('//Alice');79      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8081      // Set the default version to wrap the first message to other chains.82      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);83    });8485    await usingRelayPlaygrounds(relayUrl, async (helper) => {86      // Fund accounts on Statemint87      const relayXcmVersion = 2;88      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT, relayXcmVersion);89      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT, relayXcmVersion);90    });9192    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {93      const sovereignFundingAmount = 3_500_000_000n;9495      await helper.assets.create(96        alice,97        USDT_ASSET_ID,98        alice.address,99        USDT_ASSET_METADATA_MINIMAL_BALANCE,100      );101      await helper.assets.setMetadata(102        alice,103        USDT_ASSET_ID,104        USDT_ASSET_METADATA_NAME,105        USDT_ASSET_METADATA_DESCRIPTION,106        USDT_ASSET_METADATA_DECIMALS,107      );108      await helper.assets.mint(109        alice,110        USDT_ASSET_ID,111        alice.address,112        USDT_ASSET_AMOUNT,113      );114115      // funding parachain sovereing account on Statemint.116      // The sovereign account should be created before any action117      // (the assets pallet on Statemint check if the sovereign account exists)118      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);119      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);120    });121122123    await usingPlaygrounds(async (helper) => {124      const location = {125        V2: {126          parents: 1,127          interior: {X3: [128            {129              Parachain: STATEMINT_CHAIN,130            },131            {132              PalletInstance: STATEMINT_PALLET_INSTANCE,133            },134            {135              GeneralIndex: USDT_ASSET_ID,136            },137          ]},138        },139      };140141      const metadata =142      {143        name: USDT_ASSET_ID,144        symbol: USDT_ASSET_METADATA_NAME,145        decimals: USDT_ASSET_METADATA_DECIMALS,146        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,147      };148      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);149      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);150    });151152153    // Providing the relay currency to the unique sender account154    // (fee for USDT XCM are paid in relay tokens)155    await usingRelayPlaygrounds(relayUrl, async (helper) => {156      const destination = {157        V1: {158          parents: 0,159          interior: {X1: {160            Parachain: UNIQUE_CHAIN,161          },162          },163        }};164165      const beneficiary = {166        V1: {167          parents: 0,168          interior: {X1: {169            AccountId32: {170              network: 'Any',171              id: alice.addressRaw,172            },173          }},174        },175      };176177      const assets = {178        V1: [179          {180            id: {181              Concrete: {182                parents: 0,183                interior: 'Here',184              },185            },186            fun: {187              Fungible: TRANSFER_AMOUNT_RELAY,188            },189          },190        ],191      };192193      const feeAssetItem = 0;194195      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');196    });197198  });199200  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {201    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {202      const dest = {203        V1: {204          parents: 1,205          interior: {X1: {206            Parachain: UNIQUE_CHAIN,207          },208          },209        }};210211      const beneficiary = {212        V1: {213          parents: 0,214          interior: {X1: {215            AccountId32: {216              network: 'Any',217              id: alice.addressRaw,218            },219          }},220        },221      };222223      const assets = {224        V1: [225          {226            id: {227              Concrete: {228                parents: 0,229                interior: {230                  X2: [231                    {232                      PalletInstance: STATEMINT_PALLET_INSTANCE,233                    },234                    {235                      GeneralIndex: USDT_ASSET_ID,236                    },237                  ]},238              },239            },240            fun: {241              Fungible: TRANSFER_AMOUNT,242            },243          },244        ],245      };246247      const feeAssetItem = 0;248249      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);250      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');251252      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);253254      // common good parachain take commission in it native token255      console.log(256        '[Statemint -> Unique] transaction fees on Statemint: %s WND',257        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),258      );259      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;260261    });262263264    // ensure that asset has been delivered265    await helper.wait.newBlocks(3);266267    // expext collection id will be with id 1268    const free = await helper.ft.getBalance(1, {Substrate: alice.address});269270    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);271272    console.log(273      '[Statemint -> Unique] transaction fees on Unique: %s USDT',274      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),275    );276    console.log(277      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',278      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),279    );280    // commission has not paid in USDT token281    expect(free).to.be.equal(TRANSFER_AMOUNT);282    // ... and parachain native token283    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;284  });285286  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {287    const destination = {288      V2: {289        parents: 1,290        interior: {X2: [291          {292            Parachain: STATEMINT_CHAIN,293          },294          {295            AccountId32: {296              network: 'Any',297              id: alice.addressRaw,298            },299          },300        ]},301      },302    };303304    const relayFee = 400_000_000_000_000n;305    const currencies: [any, bigint][] = [306      [307        {308          ForeignAssetId: 0,309        },310        TRANSFER_AMOUNT,311      ],312      [313        {314          NativeAssetId: 'Parent',315        },316        relayFee,317      ],318    ];319320    const feeItem = 1;321322    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');323324    // the commission has been paid in parachain native token325    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);326    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));327    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;328329    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {330      await helper.wait.newBlocks(3);331332      // The USDT token never paid fees. Its amount not changed from begin value.333      // Also check that xcm transfer has been succeeded334      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;335    });336  });337338  itSub('Should connect and send Relay token to Unique', async ({helper}) => {339    balanceBobBefore = await helper.balance.getSubstrate(bob.address);340    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});341342    await usingRelayPlaygrounds(relayUrl, async (helper) => {343      const destination = {344        V1: {345          parents: 0,346          interior: {X1: {347            Parachain: UNIQUE_CHAIN,348          },349          },350        }};351352      const beneficiary = {353        V1: {354          parents: 0,355          interior: {X1: {356            AccountId32: {357              network: 'Any',358              id: bob.addressRaw,359            },360          }},361        },362      };363364      const assets = {365        V1: [366          {367            id: {368              Concrete: {369                parents: 0,370                interior: 'Here',371              },372            },373            fun: {374              Fungible: TRANSFER_AMOUNT_RELAY,375            },376          },377        ],378      };379380      const feeAssetItem = 0;381382      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');383    });384385    await helper.wait.newBlocks(3);386387    balanceBobAfter = await helper.balance.getSubstrate(bob.address);388    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});389390    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;391    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;392    console.log(393      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',394      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),395    );396    console.log(397      '[Relay (Westend) -> Unique] transaction fees: %s WND',398      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),399    );400    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);401    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;402    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;403  });404405  itSub('Should connect and send Relay token back', async ({helper}) => {406    let relayTokenBalanceBefore: bigint;407    let relayTokenBalanceAfter: bigint;408    await usingRelayPlaygrounds(relayUrl, async (helper) => {409      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);410    });411412    const destination = {413      V2: {414        parents: 1,415        interior: {416          X1:{417            AccountId32: {418              network: 'Any',419              id: bob.addressRaw,420            },421          },422        },423      },424    };425426    const currencies: any = [427      [428        {429          NativeAssetId: 'Parent',430        },431        TRANSFER_AMOUNT_RELAY,432      ],433    ];434435    const feeItem = 0;436437    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');438439    balanceBobFinal = await helper.balance.getSubstrate(bob.address);440    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));441442    await usingRelayPlaygrounds(relayUrl, async (helper) => {443      await helper.wait.newBlocks(10);444      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);445446      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;447      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));448      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;449    });450  });451});452453describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {454  let alice: IKeyringPair;455  let randomAccount: IKeyringPair;456457  let balanceUniqueTokenInit: bigint;458  let balanceUniqueTokenMiddle: bigint;459  let balanceUniqueTokenFinal: bigint;460  let balanceAcalaTokenInit: bigint;461  let balanceAcalaTokenMiddle: bigint;462  let balanceAcalaTokenFinal: bigint;463  let balanceUniqueForeignTokenInit: bigint;464  let balanceUniqueForeignTokenMiddle: bigint;465  let balanceUniqueForeignTokenFinal: bigint;466467  // computed by a test transfer from prod Unique to prod Acala.468  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48469  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)470  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;471472  const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;473474  before(async () => {475    await usingPlaygrounds(async (helper, privateKey) => {476      alice = await privateKey('//Alice');477      [randomAccount] = await helper.arrange.createAccounts([0n], alice);478479      // Set the default version to wrap the first message to other chains.480      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);481    });482483    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {484      const destination = {485        V1: {486          parents: 1,487          interior: {488            X1: {489              Parachain: UNIQUE_CHAIN,490            },491          },492        },493      };494495      const metadata = {496        name: 'Unique Network',497        symbol: 'UNQ',498        decimals: 18,499        minimalBalance: 1250000000000000000n,500      };501502      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);503      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);504      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);505      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});506    });507508    await usingPlaygrounds(async (helper) => {509      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);510      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);511    });512  });513514  itSub('Should connect and send UNQ to Acala', async ({helper}) => {515516    const destination = {517      V2: {518        parents: 1,519        interior: {520          X1: {521            Parachain: ACALA_CHAIN,522          },523        },524      },525    };526527    const beneficiary = {528      V2: {529        parents: 0,530        interior: {531          X1: {532            AccountId32: {533              network: 'Any',534              id: randomAccount.addressRaw,535            },536          },537        },538      },539    };540541    const assets = {542      V2: [543        {544          id: {545            Concrete: {546              parents: 0,547              interior: 'Here',548            },549          },550          fun: {551            Fungible: TRANSFER_AMOUNT,552          },553        },554      ],555    };556557    const feeAssetItem = 0;558559    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');560    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);561562    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;563    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));564    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;565566    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {567      await helper.wait.newBlocks(3);568569      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});570      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);571572      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;573      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;574      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;575576      console.log(577        '[Unique -> Acala] transaction fees on Acala: %s ACA',578        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),579      );580      console.log(581        '[Unique -> Acala] transaction fees on Acala: %s UNQ',582        helper.util.bigIntToDecimals(acaUnqFees),583      );584      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));585      expect(acaFees == 0n).to.be.true;586      expect(587        acaUnqFees == expectedAcalaIncomeFee,588        'Acala took different income fee, check the Acala foreign asset config',589      ).to.be.true;590    });591  });592593  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {594    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {595      const destination = {596        V1: {597          parents: 1,598          interior: {599            X2: [600              {Parachain: UNIQUE_CHAIN},601              {602                AccountId32: {603                  network: 'Any',604                  id: randomAccount.addressRaw,605                },606              },607            ],608          },609        },610      };611612      const id = {613        ForeignAsset: 0,614      };615616      await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');617      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);618      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);619620      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;621      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;622623      console.log(624        '[Acala -> Unique] transaction fees on Acala: %s ACA',625        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),626      );627      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));628629      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;630      expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;631    });632633    await helper.wait.newBlocks(3);634635    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);636    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;637    expect(actuallyDelivered > 0).to.be.true;638639    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));640641    const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;642    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));643    expect(unqFees == 0n).to.be.true;644  });645646  itSub('Acala can send only up to its balance', async ({helper}) => {647    // set Acala's sovereign account's balance648    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);649    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);650    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);651652    const moreThanAcalaHas = acalaBalance * 2n;653654    let targetAccountBalance = 0n;655    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);656657    const uniqueMultilocation = {658      V1: {659        parents: 1,660        interior: {661          X1: {Parachain: UNIQUE_CHAIN},662        },663      },664    };665666    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(667      targetAccount.addressRaw,668      {669        Concrete: {670          parents: 0,671          interior: 'Here',672        },673      },674      moreThanAcalaHas,675    );676677    let maliciousXcmProgramSent: any;678    const maxWaitBlocks = 3;679680    // Try to trick Unique681    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {682      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);683684      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);685    });686687    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash688        && event.outcome().isFailedToTransactAsset);689690    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);691    expect(targetAccountBalance).to.be.equal(0n);692693    // But Acala still can send the correct amount694    const validTransferAmount = acalaBalance / 2n;695    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(696      targetAccount.addressRaw,697      {698        Concrete: {699          parents: 0,700          interior: 'Here',701        },702      },703      validTransferAmount,704    );705706    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {707      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);708    });709710    await helper.wait.newBlocks(maxWaitBlocks);711712    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);713    expect(targetAccountBalance).to.be.equal(validTransferAmount);714  });715716  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {717    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);718    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);719720    const uniqueMultilocation = {721      V1: {722        parents: 1,723        interior: {724          X1: {725            Parachain: UNIQUE_CHAIN,726          },727        },728      },729    };730731    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(732      targetAccount.addressRaw,733      {734        Concrete: {735          parents: 1,736          interior: {737            X1: {738              Parachain: UNIQUE_CHAIN,739            },740          },741        },742      },743      testAmount,744    );745746    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(747      targetAccount.addressRaw,748      {749        Concrete: {750          parents: 0,751          interior: 'Here',752        },753      },754      testAmount,755    );756757    let maliciousXcmProgramFullIdSent: any;758    let maliciousXcmProgramHereIdSent: any;759    const maxWaitBlocks = 3;760761    // Try to trick Unique using full UNQ identification762    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {763      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);764765      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);766    });767768    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash769        && event.outcome().isUntrustedReserveLocation);770771    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);772    expect(accountBalance).to.be.equal(0n);773774    // Try to trick Unique using shortened UNQ identification775    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {776      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);777778      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);779    });780781    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash782        && event.outcome().isUntrustedReserveLocation);783784    accountBalance = await helper.balance.getSubstrate(targetAccount.address);785    expect(accountBalance).to.be.equal(0n);786  });787});788789// These tests are relevant only when790// the the corresponding foreign assets are not registered791describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {792  let alice: IKeyringPair;793  let alith: IKeyringPair;794795  const testAmount = 100_000_000_000n;796  let uniqueParachainJunction;797  let uniqueAccountJunction;798799  let uniqueParachainMultilocation: any;800  let uniqueAccountMultilocation: any;801  let uniqueCombinedMultilocation: any;802  let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2803804  let messageSent: any;805806  const maxWaitBlocks = 3;807808  before(async () => {809    await usingPlaygrounds(async (helper, privateKey) => {810      alice = await privateKey('//Alice');811812      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};813      uniqueAccountJunction = {814        AccountId32: {815          network: 'Any',816          id: alice.addressRaw,817        },818      };819820      uniqueParachainMultilocation = {821        V2: {822          parents: 1,823          interior: {824            X1: uniqueParachainJunction,825          },826        },827      };828829      uniqueAccountMultilocation = {830        V2: {831          parents: 0,832          interior: {833            X1: uniqueAccountJunction,834          },835        },836      };837838      uniqueCombinedMultilocation = {839        V2: {840          parents: 1,841          interior: {842            X2: [uniqueParachainJunction, uniqueAccountJunction],843          },844        },845      };846847      uniqueCombinedMultilocationAcala = {848        V1: {849          parents: 1,850          interior: {851            X2: [uniqueParachainJunction, uniqueAccountJunction],852          },853        },854      };855856      // Set the default version to wrap the first message to other chains.857      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);858    });859860    // eslint-disable-next-line require-await861    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {862      alith = helper.account.alithAccount();863    });864  });865866  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {867    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash868        && event.outcome().isFailedToTransactAsset);869  };870871  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {872    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {873      const id = {874        Token: 'ACA',875      };876      const destination = uniqueCombinedMultilocationAcala;877      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');878879      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);880    });881882    await expectFailedToTransact(helper, messageSent);883  });884885  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {886    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {887      const id = 'SelfReserve';888      const destination = uniqueCombinedMultilocation;889      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');890891      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);892    });893894    await expectFailedToTransact(helper, messageSent);895  });896897  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {898    await usingAstarPlaygrounds(astarUrl, async (helper) => {899      const destinationParachain = uniqueParachainMultilocation;900      const beneficiary = uniqueAccountMultilocation;901      const assets = {902        V2: [{903          id: {904            Concrete: {905              parents: 0,906              interior: 'Here',907            },908          },909          fun: {910            Fungible: testAmount,911          },912        }],913      };914      const feeAssetItem = 0;915916      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [917        destinationParachain,918        beneficiary,919        assets,920        feeAssetItem,921      ]);922923      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);924    });925926    await expectFailedToTransact(helper, messageSent);927  });928});929930describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {931  // Unique constants932  let alice: IKeyringPair;933  let uniqueAssetLocation;934935  let randomAccountUnique: IKeyringPair;936  let randomAccountMoonbeam: IKeyringPair;937938  // Moonbeam constants939  let assetId: string;940941  const uniqueAssetMetadata = {942    name: 'xcUnique',943    symbol: 'xcUNQ',944    decimals: 18,945    isFrozen: false,946    minimalBalance: 1n,947  };948949  let balanceUniqueTokenInit: bigint;950  let balanceUniqueTokenMiddle: bigint;951  let balanceUniqueTokenFinal: bigint;952  let balanceForeignUnqTokenInit: bigint;953  let balanceForeignUnqTokenMiddle: bigint;954  let balanceForeignUnqTokenFinal: bigint;955  let balanceGlmrTokenInit: bigint;956  let balanceGlmrTokenMiddle: bigint;957  let balanceGlmrTokenFinal: bigint;958959  before(async () => {960    await usingPlaygrounds(async (helper, privateKey) => {961      alice = await privateKey('//Alice');962      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);963964      balanceForeignUnqTokenInit = 0n;965966      // Set the default version to wrap the first message to other chains.967      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);968    });969970    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {971      const alithAccount = helper.account.alithAccount();972      const baltatharAccount = helper.account.baltatharAccount();973      const dorothyAccount = helper.account.dorothyAccount();974975      randomAccountMoonbeam = helper.account.create();976977      // >>> Sponsoring Dorothy >>>978      console.log('Sponsoring Dorothy.......');979      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);980      console.log('Sponsoring Dorothy.......DONE');981      // <<< Sponsoring Dorothy <<<982983      uniqueAssetLocation = {984        XCM: {985          parents: 1,986          interior: {X1: {Parachain: UNIQUE_CHAIN}},987        },988      };989      const existentialDeposit = 1n;990      const isSufficient = true;991      const unitsPerSecond = 1n;992      const numAssetsWeightHint = 0;993994      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({995        location: uniqueAssetLocation,996        metadata: uniqueAssetMetadata,997        existentialDeposit,998        isSufficient,999        unitsPerSecond,1000        numAssetsWeightHint,1001      });10021003      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);10041005      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);10061007      // >>> Acquire Unique AssetId Info on Moonbeam >>>1008      console.log('Acquire Unique AssetId Info on Moonbeam.......');10091010      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();10111012      console.log('UNQ asset ID is %s', assetId);1013      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1014      // >>> Acquire Unique AssetId Info on Moonbeam >>>10151016      // >>> Sponsoring random Account >>>1017      console.log('Sponsoring random Account.......');1018      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1019      console.log('Sponsoring random Account.......DONE');1020      // <<< Sponsoring random Account <<<10211022      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1023    });10241025    await usingPlaygrounds(async (helper) => {1026      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1027      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1028    });1029  });10301031  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1032    const currencyId = {1033      NativeAssetId: 'Here',1034    };1035    const dest = {1036      V2: {1037        parents: 1,1038        interior: {1039          X2: [1040            {Parachain: MOONBEAM_CHAIN},1041            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1042          ],1043        },1044      },1045    };1046    const amount = TRANSFER_AMOUNT;10471048    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');10491050    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1051    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;10521053    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1054    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1055    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;10561057    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1058      await helper.wait.newBlocks(3);10591060      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);10611062      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1063      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1064      expect(glmrFees == 0n).to.be.true;10651066      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;10671068      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1069      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1070      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1071    });1072  });10731074  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1075    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1076      const asset = {1077        V2: {1078          id: {1079            Concrete: {1080              parents: 1,1081              interior: {1082                X1: {Parachain: UNIQUE_CHAIN},1083              },1084            },1085          },1086          fun: {1087            Fungible: TRANSFER_AMOUNT,1088          },1089        },1090      };1091      const destination = {1092        V2: {1093          parents: 1,1094          interior: {1095            X2: [1096              {Parachain: UNIQUE_CHAIN},1097              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1098            ],1099          },1100        },1101      };11021103      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');11041105      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);11061107      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1108      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1109      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;11101111      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);11121113      expect(unqRandomAccountAsset).to.be.null;11141115      balanceForeignUnqTokenFinal = 0n;11161117      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1118      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1119      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1120    });11211122    await helper.wait.newBlocks(3);11231124    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1125    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1126    expect(actuallyDelivered > 0).to.be.true;11271128    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));11291130    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1131    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1132    expect(unqFees == 0n).to.be.true;1133  });11341135  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1136    // set Moonbeam's sovereign account's balance1137    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1138    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1139    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);11401141    const moreThanMoonbeamHas = moonbeamBalance * 2n;11421143    let targetAccountBalance = 0n;1144    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11451146    const uniqueMultilocation = {1147      V2: {1148        parents: 1,1149        interior: {1150          X1: {Parachain: UNIQUE_CHAIN},1151        },1152      },1153    };11541155    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1156      targetAccount.addressRaw,1157      {1158        Concrete: {1159          parents: 0,1160          interior: 'Here',1161        },1162      },1163      moreThanMoonbeamHas,1164    );11651166    let maliciousXcmProgramSent: any;1167    const maxWaitBlocks = 3;11681169    // Try to trick Unique1170    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1171      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);11721173      // Needed to bypass the call filter.1174      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1175      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);11761177      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1178    });11791180    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1181        && event.outcome().isFailedToTransactAsset);11821183    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1184    expect(targetAccountBalance).to.be.equal(0n);11851186    // But Moonbeam still can send the correct amount1187    const validTransferAmount = moonbeamBalance / 2n;1188    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1189      targetAccount.addressRaw,1190      {1191        Concrete: {1192          parents: 0,1193          interior: 'Here',1194        },1195      },1196      validTransferAmount,1197    );11981199    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1200      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);12011202      // Needed to bypass the call filter.1203      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1204      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1205    });12061207    await helper.wait.newBlocks(maxWaitBlocks);12081209    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1210    expect(targetAccountBalance).to.be.equal(validTransferAmount);1211  });12121213  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1214    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1215    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12161217    const uniqueMultilocation = {1218      V2: {1219        parents: 1,1220        interior: {1221          X1: {1222            Parachain: UNIQUE_CHAIN,1223          },1224        },1225      },1226    };12271228    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1229      targetAccount.addressRaw,1230      {1231        Concrete: {1232          parents: 0,1233          interior: {1234            X1: {1235              Parachain: UNIQUE_CHAIN,1236            },1237          },1238        },1239      },1240      testAmount,1241    );12421243    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1244      targetAccount.addressRaw,1245      {1246        Concrete: {1247          parents: 0,1248          interior: 'Here',1249        },1250      },1251      testAmount,1252    );12531254    let maliciousXcmProgramFullIdSent: any;1255    let maliciousXcmProgramHereIdSent: any;1256    const maxWaitBlocks = 3;12571258    // Try to trick Unique using full UNQ identification1259    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1260      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);12611262      // Needed to bypass the call filter.1263      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1264      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);12651266      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1267    });12681269    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1270        && event.outcome().isUntrustedReserveLocation);12711272    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1273    expect(accountBalance).to.be.equal(0n);12741275    // Try to trick Unique using shortened UNQ identification1276    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1277      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);12781279      // Needed to bypass the call filter.1280      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1281      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);12821283      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1284    });12851286    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1287        && event.outcome().isUntrustedReserveLocation);12881289    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1290    expect(accountBalance).to.be.equal(0n);1291  });1292});12931294describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1295  let alice: IKeyringPair;1296  let randomAccount: IKeyringPair;12971298  const UNQ_ASSET_ID_ON_ASTAR = 1;1299  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;13001301  // Unique -> Astar1302  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1303  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1304  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1305  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens13061307  // Astar -> Unique1308  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1309  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ13101311  let balanceAfterUniqueToAstarXCM: bigint;13121313  before(async () => {1314    await usingPlaygrounds(async (helper, privateKey) => {1315      alice = await privateKey('//Alice');1316      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1317      console.log('randomAccount', randomAccount.address);13181319      // Set the default version to wrap the first message to other chains.1320      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1321    });13221323    await usingAstarPlaygrounds(astarUrl, async (helper) => {1324      console.log('1. Create foreign asset and metadata');1325      // TODO update metadata with values from production1326      await helper.assets.create(1327        alice,1328        UNQ_ASSET_ID_ON_ASTAR,1329        alice.address,1330        UNQ_MINIMAL_BALANCE_ON_ASTAR,1331      );13321333      await helper.assets.setMetadata(1334        alice,1335        UNQ_ASSET_ID_ON_ASTAR,1336        'Cross chain UNQ',1337        'xcUNQ',1338        Number(UNQ_DECIMALS),1339      );13401341      console.log('2. Register asset location on Astar');1342      const assetLocation = {1343        V2: {1344          parents: 1,1345          interior: {1346            X1: {1347              Parachain: UNIQUE_CHAIN,1348            },1349          },1350        },1351      };13521353      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);13541355      console.log('3. Set UNQ payment for XCM execution on Astar');1356      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);13571358      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1359      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1360    });1361  });13621363  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1364    const destination = {1365      V2: {1366        parents: 1,1367        interior: {1368          X1: {1369            Parachain: ASTAR_CHAIN,1370          },1371        },1372      },1373    };13741375    const beneficiary = {1376      V2: {1377        parents: 0,1378        interior: {1379          X1: {1380            AccountId32: {1381              network: 'Any',1382              id: randomAccount.addressRaw,1383            },1384          },1385        },1386      },1387    };13881389    const assets = {1390      V2: [1391        {1392          id: {1393            Concrete: {1394              parents: 0,1395              interior: 'Here',1396            },1397          },1398          fun: {1399            Fungible: unqToAstarTransferred,1400          },1401        },1402      ],1403    };14041405    // Initial balance is 100 UNQ1406    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1407    console.log(`Initial balance is: ${balanceBefore}`);14081409    const feeAssetItem = 0;1410    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14111412    // Balance after reserve transfer is less than 901413    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1414    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1415    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1416    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;14171418    await usingAstarPlaygrounds(astarUrl, async (helper) => {1419      await helper.wait.newBlocks(3);1420      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1421      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);14221423      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1424      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);14251426      expect(xcUNQbalance).to.eq(unqToAstarArrived);1427      // Astar balance does not changed1428      expect(astarBalance).to.eq(astarInitialBalance);1429    });1430  });14311432  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1433    await usingAstarPlaygrounds(astarUrl, async (helper) => {1434      const destination = {1435        V2: {1436          parents: 1,1437          interior: {1438            X1: {1439              Parachain: UNIQUE_CHAIN,1440            },1441          },1442        },1443      };14441445      const beneficiary = {1446        V2: {1447          parents: 0,1448          interior: {1449            X1: {1450              AccountId32: {1451                network: 'Any',1452                id: randomAccount.addressRaw,1453              },1454            },1455          },1456        },1457      };14581459      const assets = {1460        V2: [1461          {1462            id: {1463              Concrete: {1464                parents: 1,1465                interior: {1466                  X1: {1467                    Parachain: UNIQUE_CHAIN,1468                  },1469                },1470              },1471            },1472            fun: {1473              Fungible: unqFromAstarTransfered,1474            },1475          },1476        ],1477      };14781479      // Initial balance is 1 ASTR1480      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1481      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1482      expect(balanceASTRbefore).to.eq(astarInitialBalance);14831484      const feeAssetItem = 0;1485      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1486      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);14871488      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1489      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1490      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);14911492      // Assert: xcUNQ balance correctly decreased1493      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1494      // Assert: ASTR balance is 0.996...1495      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1496    });14971498    await helper.wait.newBlocks(3);1499    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1500    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1501    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1502  });15031504  itSub('Astar can send only up to its balance', async ({helper}) => {1505    // set Astar's sovereign account's balance1506    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1507    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1508    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);15091510    const moreThanAstarHas = astarBalance * 2n;15111512    let targetAccountBalance = 0n;1513    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15141515    const uniqueMultilocation = {1516      V2: {1517        parents: 1,1518        interior: {1519          X1: {Parachain: UNIQUE_CHAIN},1520        },1521      },1522    };15231524    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1525      targetAccount.addressRaw,1526      {1527        Concrete: {1528          parents: 0,1529          interior: 'Here',1530        },1531      },1532      moreThanAstarHas,1533    );15341535    let maliciousXcmProgramSent: any;1536    const maxWaitBlocks = 3;15371538    // Try to trick Unique1539    await usingAstarPlaygrounds(astarUrl, async (helper) => {1540      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);15411542      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1543    });15441545    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1546        && event.outcome().isFailedToTransactAsset);15471548    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1549    expect(targetAccountBalance).to.be.equal(0n);15501551    // But Astar still can send the correct amount1552    const validTransferAmount = astarBalance / 2n;1553    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1554      targetAccount.addressRaw,1555      {1556        Concrete: {1557          parents: 0,1558          interior: 'Here',1559        },1560      },1561      validTransferAmount,1562    );15631564    await usingAstarPlaygrounds(astarUrl, async (helper) => {1565      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1566    });15671568    await helper.wait.newBlocks(maxWaitBlocks);15691570    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1571    expect(targetAccountBalance).to.be.equal(validTransferAmount);1572  });15731574  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1575    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1576    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15771578    const uniqueMultilocation = {1579      V2: {1580        parents: 1,1581        interior: {1582          X1: {1583            Parachain: UNIQUE_CHAIN,1584          },1585        },1586      },1587    };15881589    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1590      targetAccount.addressRaw,1591      {1592        Concrete: {1593          parents: 1,1594          interior: {1595            X1: {1596              Parachain: UNIQUE_CHAIN,1597            },1598          },1599        },1600      },1601      testAmount,1602    );16031604    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1605      targetAccount.addressRaw,1606      {1607        Concrete: {1608          parents: 0,1609          interior: 'Here',1610        },1611      },1612      testAmount,1613    );16141615    let maliciousXcmProgramFullIdSent: any;1616    let maliciousXcmProgramHereIdSent: any;1617    const maxWaitBlocks = 3;16181619    // Try to trick Unique using full UNQ identification1620    await usingAstarPlaygrounds(astarUrl, async (helper) => {1621      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);16221623      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1624    });16251626    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1627        && event.outcome().isUntrustedReserveLocation);16281629    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1630    expect(accountBalance).to.be.equal(0n);16311632    // Try to trick Unique using shortened UNQ identification1633    await usingAstarPlaygrounds(astarUrl, async (helper) => {1634      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);16351636      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1637    });16381639    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1640        && event.outcome().isUntrustedReserveLocation);16411642    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1643    expect(accountBalance).to.be.equal(0n);1644  });1645});