git.delta.rocks / unique-network / refs/commits / 8545aff0545e

difftreelog

source

tests/src/xcm/xcmUnique.test.ts53.7 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.messageHash()688        && 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.messageHash()769        && 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.messageHash()782        && 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;802803  let messageSent: any;804805  const maxWaitBlocks = 3;806807  before(async () => {808    await usingPlaygrounds(async (helper, privateKey) => {809      alice = await privateKey('//Alice');810811      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};812      uniqueAccountJunction = {813        AccountId32: {814          network: 'Any',815          id: alice.addressRaw,816        },817      };818819      uniqueParachainMultilocation = {820        V1: {821          parents: 1,822          interior: {823            X1: uniqueParachainJunction,824          },825        },826      };827828      uniqueAccountMultilocation = {829        V1: {830          parents: 0,831          interior: {832            X1: uniqueAccountJunction,833          },834        },835      };836837      uniqueCombinedMultilocation = {838        V1: {839          parents: 1,840          interior: {841            X2: [uniqueParachainJunction, uniqueAccountJunction],842          },843        },844      };845846      // Set the default version to wrap the first message to other chains.847      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);848    });849850    // eslint-disable-next-line require-await851    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {852      alith = helper.account.alithAccount();853    });854  });855856  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {857    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == messageSent.messageHash()858        && event.outcome().isFailedToTransactAsset);859  };860861  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {862    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {863      const id = {864        Token: 'ACA',865      };866      const destination = uniqueCombinedMultilocation;867      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');868869      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);870    });871872    await expectFailedToTransact(helper, messageSent);873  });874875  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {876    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {877      const id = 'SelfReserve';878      const destination = uniqueCombinedMultilocation;879      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');880881      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);882    });883884    await expectFailedToTransact(helper, messageSent);885  });886887  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {888    await usingAstarPlaygrounds(astarUrl, async (helper) => {889      const destinationParachain = uniqueParachainMultilocation;890      const beneficiary = uniqueAccountMultilocation;891      const assets = {892        V1: [{893          id: {894            Concrete: {895              parents: 0,896              interior: 'Here',897            },898          },899          fun: {900            Fungible: testAmount,901          },902        }],903      };904      const feeAssetItem = 0;905906      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [907        destinationParachain,908        beneficiary,909        assets,910        feeAssetItem,911      ]);912913      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);914    });915916    await expectFailedToTransact(helper, messageSent);917  });918});919920describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {921  // Unique constants922  let alice: IKeyringPair;923  let uniqueAssetLocation;924925  let randomAccountUnique: IKeyringPair;926  let randomAccountMoonbeam: IKeyringPair;927928  // Moonbeam constants929  let assetId: string;930931  const uniqueAssetMetadata = {932    name: 'xcUnique',933    symbol: 'xcUNQ',934    decimals: 18,935    isFrozen: false,936    minimalBalance: 1n,937  };938939  let balanceUniqueTokenInit: bigint;940  let balanceUniqueTokenMiddle: bigint;941  let balanceUniqueTokenFinal: bigint;942  let balanceForeignUnqTokenInit: bigint;943  let balanceForeignUnqTokenMiddle: bigint;944  let balanceForeignUnqTokenFinal: bigint;945  let balanceGlmrTokenInit: bigint;946  let balanceGlmrTokenMiddle: bigint;947  let balanceGlmrTokenFinal: bigint;948949  before(async () => {950    await usingPlaygrounds(async (helper, privateKey) => {951      alice = await privateKey('//Alice');952      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);953954      balanceForeignUnqTokenInit = 0n;955956      // Set the default version to wrap the first message to other chains.957      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);958    });959960    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {961      const alithAccount = helper.account.alithAccount();962      const baltatharAccount = helper.account.baltatharAccount();963      const dorothyAccount = helper.account.dorothyAccount();964965      randomAccountMoonbeam = helper.account.create();966967      // >>> Sponsoring Dorothy >>>968      console.log('Sponsoring Dorothy.......');969      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);970      console.log('Sponsoring Dorothy.......DONE');971      // <<< Sponsoring Dorothy <<<972973      uniqueAssetLocation = {974        XCM: {975          parents: 1,976          interior: {X1: {Parachain: UNIQUE_CHAIN}},977        },978      };979      const existentialDeposit = 1n;980      const isSufficient = true;981      const unitsPerSecond = 1n;982      const numAssetsWeightHint = 0;983984      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({985        location: uniqueAssetLocation,986        metadata: uniqueAssetMetadata,987        existentialDeposit,988        isSufficient,989        unitsPerSecond,990        numAssetsWeightHint,991      });992993      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);994995      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);996997      // >>> Acquire Unique AssetId Info on Moonbeam >>>998      console.log('Acquire Unique AssetId Info on Moonbeam.......');9991000      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();10011002      console.log('UNQ asset ID is %s', assetId);1003      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1004      // >>> Acquire Unique AssetId Info on Moonbeam >>>10051006      // >>> Sponsoring random Account >>>1007      console.log('Sponsoring random Account.......');1008      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1009      console.log('Sponsoring random Account.......DONE');1010      // <<< Sponsoring random Account <<<10111012      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1013    });10141015    await usingPlaygrounds(async (helper) => {1016      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1017      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1018    });1019  });10201021  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1022    const currencyId = {1023      NativeAssetId: 'Here',1024    };1025    const dest = {1026      V2: {1027        parents: 1,1028        interior: {1029          X2: [1030            {Parachain: MOONBEAM_CHAIN},1031            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1032          ],1033        },1034      },1035    };1036    const amount = TRANSFER_AMOUNT;10371038    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');10391040    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1041    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;10421043    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1044    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1045    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;10461047    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1048      await helper.wait.newBlocks(3);10491050      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);10511052      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1053      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1054      expect(glmrFees == 0n).to.be.true;10551056      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;10571058      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1059      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1060      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1061    });1062  });10631064  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1065    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1066      const asset = {1067        V1: {1068          id: {1069            Concrete: {1070              parents: 1,1071              interior: {1072                X1: {Parachain: UNIQUE_CHAIN},1073              },1074            },1075          },1076          fun: {1077            Fungible: TRANSFER_AMOUNT,1078          },1079        },1080      };1081      const destination = {1082        V1: {1083          parents: 1,1084          interior: {1085            X2: [1086              {Parachain: UNIQUE_CHAIN},1087              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1088            ],1089          },1090        },1091      };10921093      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');10941095      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);10961097      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1098      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1099      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;11001101      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);11021103      expect(unqRandomAccountAsset).to.be.null;11041105      balanceForeignUnqTokenFinal = 0n;11061107      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1108      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1109      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1110    });11111112    await helper.wait.newBlocks(3);11131114    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1115    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1116    expect(actuallyDelivered > 0).to.be.true;11171118    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));11191120    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1121    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1122    expect(unqFees == 0n).to.be.true;1123  });11241125  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1126    // set Moonbeam's sovereign account's balance1127    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1128    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1129    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);11301131    const moreThanMoonbeamHas = moonbeamBalance * 2n;11321133    let targetAccountBalance = 0n;1134    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11351136    const uniqueMultilocation = {1137      V1: {1138        parents: 1,1139        interior: {1140          X1: {Parachain: UNIQUE_CHAIN},1141        },1142      },1143    };11441145    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1146      targetAccount.addressRaw,1147      {1148        Concrete: {1149          parents: 0,1150          interior: 'Here',1151        },1152      },1153      moreThanMoonbeamHas,1154    );11551156    let maliciousXcmProgramSent: any;1157    const maxWaitBlocks = 3;11581159    // Try to trick Unique1160    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1161      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);11621163      // Needed to bypass the call filter.1164      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1165      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);11661167      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1168    });11691170    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()1171        && event.outcome().isFailedToTransactAsset);11721173    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1174    expect(targetAccountBalance).to.be.equal(0n);11751176    // But Moonbeam still can send the correct amount1177    const validTransferAmount = moonbeamBalance / 2n;1178    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1179      targetAccount.addressRaw,1180      {1181        Concrete: {1182          parents: 0,1183          interior: 'Here',1184        },1185      },1186      validTransferAmount,1187    );11881189    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1190      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);11911192      // Needed to bypass the call filter.1193      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1194      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1195    });11961197    await helper.wait.newBlocks(maxWaitBlocks);11981199    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1200    expect(targetAccountBalance).to.be.equal(validTransferAmount);1201  });12021203  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1204    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1205    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12061207    const uniqueMultilocation = {1208      V1: {1209        parents: 1,1210        interior: {1211          X1: {1212            Parachain: UNIQUE_CHAIN,1213          },1214        },1215      },1216    };12171218    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1219      targetAccount.addressRaw,1220      {1221        Concrete: {1222          parents: 0,1223          interior: {1224            X1: {1225              Parachain: UNIQUE_CHAIN,1226            },1227          },1228        },1229      },1230      testAmount,1231    );12321233    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1234      targetAccount.addressRaw,1235      {1236        Concrete: {1237          parents: 0,1238          interior: 'Here',1239        },1240      },1241      testAmount,1242    );12431244    let maliciousXcmProgramFullIdSent: any;1245    let maliciousXcmProgramHereIdSent: any;1246    const maxWaitBlocks = 3;12471248    // Try to trick Unique using full UNQ identification1249    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1250      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);12511252      // Needed to bypass the call filter.1253      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1254      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);12551256      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1257    });12581259    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()1260        && event.outcome().isUntrustedReserveLocation);12611262    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1263    expect(accountBalance).to.be.equal(0n);12641265    // Try to trick Unique using shortened UNQ identification1266    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1267      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);12681269      // Needed to bypass the call filter.1270      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1271      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);12721273      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1274    });12751276    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()1277        && event.outcome().isUntrustedReserveLocation);12781279    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1280    expect(accountBalance).to.be.equal(0n);1281  });1282});12831284describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1285  let alice: IKeyringPair;1286  let randomAccount: IKeyringPair;12871288  const UNQ_ASSET_ID_ON_ASTAR = 1;1289  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;12901291  // Unique -> Astar1292  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1293  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1294  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1295  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens12961297  // Astar -> Unique1298  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1299  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ13001301  let balanceAfterUniqueToAstarXCM: bigint;13021303  before(async () => {1304    await usingPlaygrounds(async (helper, privateKey) => {1305      alice = await privateKey('//Alice');1306      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1307      console.log('randomAccount', randomAccount.address);13081309      // Set the default version to wrap the first message to other chains.1310      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1311    });13121313    await usingAstarPlaygrounds(astarUrl, async (helper) => {1314      console.log('1. Create foreign asset and metadata');1315      // TODO update metadata with values from production1316      await helper.assets.create(1317        alice,1318        UNQ_ASSET_ID_ON_ASTAR,1319        alice.address,1320        UNQ_MINIMAL_BALANCE_ON_ASTAR,1321      );13221323      await helper.assets.setMetadata(1324        alice,1325        UNQ_ASSET_ID_ON_ASTAR,1326        'Cross chain UNQ',1327        'xcUNQ',1328        Number(UNQ_DECIMALS),1329      );13301331      console.log('2. Register asset location on Astar');1332      const assetLocation = {1333        V1: {1334          parents: 1,1335          interior: {1336            X1: {1337              Parachain: UNIQUE_CHAIN,1338            },1339          },1340        },1341      };13421343      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);13441345      console.log('3. Set UNQ payment for XCM execution on Astar');1346      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);13471348      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1349      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1350    });1351  });13521353  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1354    const destination = {1355      V2: {1356        parents: 1,1357        interior: {1358          X1: {1359            Parachain: ASTAR_CHAIN,1360          },1361        },1362      },1363    };13641365    const beneficiary = {1366      V2: {1367        parents: 0,1368        interior: {1369          X1: {1370            AccountId32: {1371              network: 'Any',1372              id: randomAccount.addressRaw,1373            },1374          },1375        },1376      },1377    };13781379    const assets = {1380      V2: [1381        {1382          id: {1383            Concrete: {1384              parents: 0,1385              interior: 'Here',1386            },1387          },1388          fun: {1389            Fungible: unqToAstarTransferred,1390          },1391        },1392      ],1393    };13941395    // Initial balance is 100 UNQ1396    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1397    console.log(`Initial balance is: ${balanceBefore}`);13981399    const feeAssetItem = 0;1400    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14011402    // Balance after reserve transfer is less than 901403    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1404    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1405    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1406    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;14071408    await usingAstarPlaygrounds(astarUrl, async (helper) => {1409      await helper.wait.newBlocks(3);1410      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1411      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);14121413      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1414      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);14151416      expect(xcUNQbalance).to.eq(unqToAstarArrived);1417      // Astar balance does not changed1418      expect(astarBalance).to.eq(astarInitialBalance);1419    });1420  });14211422  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1423    await usingAstarPlaygrounds(astarUrl, async (helper) => {1424      const destination = {1425        V1: {1426          parents: 1,1427          interior: {1428            X1: {1429              Parachain: UNIQUE_CHAIN,1430            },1431          },1432        },1433      };14341435      const beneficiary = {1436        V1: {1437          parents: 0,1438          interior: {1439            X1: {1440              AccountId32: {1441                network: 'Any',1442                id: randomAccount.addressRaw,1443              },1444            },1445          },1446        },1447      };14481449      const assets = {1450        V1: [1451          {1452            id: {1453              Concrete: {1454                parents: 1,1455                interior: {1456                  X1: {1457                    Parachain: UNIQUE_CHAIN,1458                  },1459                },1460              },1461            },1462            fun: {1463              Fungible: unqFromAstarTransfered,1464            },1465          },1466        ],1467      };14681469      // Initial balance is 1 ASTR1470      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1471      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1472      expect(balanceASTRbefore).to.eq(astarInitialBalance);14731474      const feeAssetItem = 0;1475      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1476      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);14771478      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1479      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1480      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);14811482      // Assert: xcUNQ balance correctly decreased1483      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1484      // Assert: ASTR balance is 0.996...1485      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1486    });14871488    await helper.wait.newBlocks(3);1489    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1490    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1491    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1492  });14931494  itSub('Astar can send only up to its balance', async ({helper}) => {1495    // set Astar's sovereign account's balance1496    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1497    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1498    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);14991500    const moreThanAstarHas = astarBalance * 2n;15011502    let targetAccountBalance = 0n;1503    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15041505    const uniqueMultilocation = {1506      V1: {1507        parents: 1,1508        interior: {1509          X1: {Parachain: UNIQUE_CHAIN},1510        },1511      },1512    };15131514    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1515      targetAccount.addressRaw,1516      {1517        Concrete: {1518          parents: 0,1519          interior: 'Here',1520        },1521      },1522      moreThanAstarHas,1523    );15241525    let maliciousXcmProgramSent: any;1526    const maxWaitBlocks = 3;15271528    // Try to trick Unique1529    await usingAstarPlaygrounds(astarUrl, async (helper) => {1530      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);15311532      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1533    });15341535    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramSent.messageHash()1536        && event.outcome().isFailedToTransactAsset);15371538    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1539    expect(targetAccountBalance).to.be.equal(0n);15401541    // But Astar still can send the correct amount1542    const validTransferAmount = astarBalance / 2n;1543    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1544      targetAccount.addressRaw,1545      {1546        Concrete: {1547          parents: 0,1548          interior: 'Here',1549        },1550      },1551      validTransferAmount,1552    );15531554    await usingAstarPlaygrounds(astarUrl, async (helper) => {1555      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1556    });15571558    await helper.wait.newBlocks(maxWaitBlocks);15591560    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1561    expect(targetAccountBalance).to.be.equal(validTransferAmount);1562  });15631564  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1565    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1566    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15671568    const uniqueMultilocation = {1569      V1: {1570        parents: 1,1571        interior: {1572          X1: {1573            Parachain: UNIQUE_CHAIN,1574          },1575        },1576      },1577    };15781579    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1580      targetAccount.addressRaw,1581      {1582        Concrete: {1583          parents: 1,1584          interior: {1585            X1: {1586              Parachain: UNIQUE_CHAIN,1587            },1588          },1589        },1590      },1591      testAmount,1592    );15931594    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1595      targetAccount.addressRaw,1596      {1597        Concrete: {1598          parents: 0,1599          interior: 'Here',1600        },1601      },1602      testAmount,1603    );16041605    let maliciousXcmProgramFullIdSent: any;1606    let maliciousXcmProgramHereIdSent: any;1607    const maxWaitBlocks = 3;16081609    // Try to trick Unique using full UNQ identification1610    await usingAstarPlaygrounds(astarUrl, async (helper) => {1611      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);16121613      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1614    });16151616    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramFullIdSent.messageHash()1617        && event.outcome().isUntrustedReserveLocation);16181619    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1620    expect(accountBalance).to.be.equal(0n);16211622    // Try to trick Unique using shortened UNQ identification1623    await usingAstarPlaygrounds(astarUrl, async (helper) => {1624      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);16251626      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1627    });16281629    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash() == maliciousXcmProgramHereIdSent.messageHash()1630        && event.outcome().isUntrustedReserveLocation);16311632    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1633    expect(accountBalance).to.be.equal(0n);1634  });16351636});