git.delta.rocks / unique-network / refs/commits / 22a4d5338189

difftreelog

fix remove .only

Daniel Shiposha2023-04-13parent: #603147b.patch.diff
in: master

1 file changed

modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
before · tests/src/xcm/xcmUnique.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {XcmV2TraitsError} from '../interfaces';20import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';21import {DevUniqueHelper} from '../util/playgrounds/unique.dev';2223const UNIQUE_CHAIN = 2037;24const STATEMINT_CHAIN = 1000;25const ACALA_CHAIN = 2000;26const MOONBEAM_CHAIN = 2004;27const ASTAR_CHAIN = 2006;2829const STATEMINT_PALLET_INSTANCE = 50;3031const relayUrl = config.relayUrl;32const statemintUrl = config.statemintUrl;33const acalaUrl = config.acalaUrl;34const moonbeamUrl = config.moonbeamUrl;35const astarUrl = config.astarUrl;3637const RELAY_DECIMALS = 12;38const STATEMINT_DECIMALS = 12;39const ACALA_DECIMALS = 12;40const ASTAR_DECIMALS = 18n;41const UNQ_DECIMALS = 18n;4243const TRANSFER_AMOUNT = 2000000000000000000000000n;4445const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4647const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4849const USDT_ASSET_ID = 100;50const USDT_ASSET_METADATA_DECIMALS = 18;51const USDT_ASSET_METADATA_NAME = 'USDT';52const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';53const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;54const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5556const SAFE_XCM_VERSION = 2;5758describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {59  let alice: IKeyringPair;60  let bob: IKeyringPair;6162  let balanceStmnBefore: bigint;63  let balanceStmnAfter: bigint;6465  let balanceUniqueBefore: bigint;66  let balanceUniqueAfter: bigint;67  let balanceUniqueFinal: bigint;6869  let balanceBobBefore: bigint;70  let balanceBobAfter: bigint;71  let balanceBobFinal: bigint;7273  let balanceBobRelayTokenBefore: bigint;74  let balanceBobRelayTokenAfter: bigint;757677  before(async () => {78    await usingPlaygrounds(async (helper, privateKey) => {79      alice = await privateKey('//Alice');80      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8182      // Set the default version to wrap the first message to other chains.83      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);84    });8586    await usingRelayPlaygrounds(relayUrl, async (helper) => {87      // Fund accounts on Statemint88      const relayXcmVersion = 2;89      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT, relayXcmVersion);90      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT, relayXcmVersion);91    });9293    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {94      const sovereignFundingAmount = 3_500_000_000n;9596      await helper.assets.create(97        alice,98        USDT_ASSET_ID,99        alice.address,100        USDT_ASSET_METADATA_MINIMAL_BALANCE,101      );102      await helper.assets.setMetadata(103        alice,104        USDT_ASSET_ID,105        USDT_ASSET_METADATA_NAME,106        USDT_ASSET_METADATA_DESCRIPTION,107        USDT_ASSET_METADATA_DECIMALS,108      );109      await helper.assets.mint(110        alice,111        USDT_ASSET_ID,112        alice.address,113        USDT_ASSET_AMOUNT,114      );115116      // funding parachain sovereing account on Statemint.117      // The sovereign account should be created before any action118      // (the assets pallet on Statemint check if the sovereign account exists)119      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);120      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);121    });122123124    await usingPlaygrounds(async (helper) => {125      const location = {126        V2: {127          parents: 1,128          interior: {X3: [129            {130              Parachain: STATEMINT_CHAIN,131            },132            {133              PalletInstance: STATEMINT_PALLET_INSTANCE,134            },135            {136              GeneralIndex: USDT_ASSET_ID,137            },138          ]},139        },140      };141142      const metadata =143      {144        name: USDT_ASSET_ID,145        symbol: USDT_ASSET_METADATA_NAME,146        decimals: USDT_ASSET_METADATA_DECIMALS,147        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,148      };149      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);150      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);151    });152153154    // Providing the relay currency to the unique sender account155    // (fee for USDT XCM are paid in relay tokens)156    await usingRelayPlaygrounds(relayUrl, async (helper) => {157      const destination = {158        V1: {159          parents: 0,160          interior: {X1: {161            Parachain: UNIQUE_CHAIN,162          },163          },164        }};165166      const beneficiary = {167        V1: {168          parents: 0,169          interior: {X1: {170            AccountId32: {171              network: 'Any',172              id: alice.addressRaw,173            },174          }},175        },176      };177178      const assets = {179        V1: [180          {181            id: {182              Concrete: {183                parents: 0,184                interior: 'Here',185              },186            },187            fun: {188              Fungible: TRANSFER_AMOUNT_RELAY,189            },190          },191        ],192      };193194      const feeAssetItem = 0;195196      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');197    });198199  });200201  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {202    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {203      const dest = {204        V1: {205          parents: 1,206          interior: {X1: {207            Parachain: UNIQUE_CHAIN,208          },209          },210        }};211212      const beneficiary = {213        V1: {214          parents: 0,215          interior: {X1: {216            AccountId32: {217              network: 'Any',218              id: alice.addressRaw,219            },220          }},221        },222      };223224      const assets = {225        V1: [226          {227            id: {228              Concrete: {229                parents: 0,230                interior: {231                  X2: [232                    {233                      PalletInstance: STATEMINT_PALLET_INSTANCE,234                    },235                    {236                      GeneralIndex: USDT_ASSET_ID,237                    },238                  ]},239              },240            },241            fun: {242              Fungible: TRANSFER_AMOUNT,243            },244          },245        ],246      };247248      const feeAssetItem = 0;249250      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);251      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');252253      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);254255      // common good parachain take commission in it native token256      console.log(257        '[Statemint -> Unique] transaction fees on Statemint: %s WND',258        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),259      );260      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;261262    });263264265    // ensure that asset has been delivered266    await helper.wait.newBlocks(3);267268    // expext collection id will be with id 1269    const free = await helper.ft.getBalance(1, {Substrate: alice.address});270271    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);272273    console.log(274      '[Statemint -> Unique] transaction fees on Unique: %s USDT',275      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),276    );277    console.log(278      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',279      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),280    );281    // commission has not paid in USDT token282    expect(free).to.be.equal(TRANSFER_AMOUNT);283    // ... and parachain native token284    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;285  });286287  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {288    const destination = {289      V2: {290        parents: 1,291        interior: {X2: [292          {293            Parachain: STATEMINT_CHAIN,294          },295          {296            AccountId32: {297              network: 'Any',298              id: alice.addressRaw,299            },300          },301        ]},302      },303    };304305    const relayFee = 400_000_000_000_000n;306    const currencies: [any, bigint][] = [307      [308        {309          ForeignAssetId: 0,310        },311        TRANSFER_AMOUNT,312      ],313      [314        {315          NativeAssetId: 'Parent',316        },317        relayFee,318      ],319    ];320321    const feeItem = 1;322323    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');324325    // the commission has been paid in parachain native token326    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);327    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));328    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;329330    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {331      await helper.wait.newBlocks(3);332333      // The USDT token never paid fees. Its amount not changed from begin value.334      // Also check that xcm transfer has been succeeded335      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;336    });337  });338339  itSub('Should connect and send Relay token to Unique', async ({helper}) => {340    balanceBobBefore = await helper.balance.getSubstrate(bob.address);341    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});342343    await usingRelayPlaygrounds(relayUrl, async (helper) => {344      const destination = {345        V1: {346          parents: 0,347          interior: {X1: {348            Parachain: UNIQUE_CHAIN,349          },350          },351        }};352353      const beneficiary = {354        V1: {355          parents: 0,356          interior: {X1: {357            AccountId32: {358              network: 'Any',359              id: bob.addressRaw,360            },361          }},362        },363      };364365      const assets = {366        V1: [367          {368            id: {369              Concrete: {370                parents: 0,371                interior: 'Here',372              },373            },374            fun: {375              Fungible: TRANSFER_AMOUNT_RELAY,376            },377          },378        ],379      };380381      const feeAssetItem = 0;382383      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');384    });385386    await helper.wait.newBlocks(3);387388    balanceBobAfter = await helper.balance.getSubstrate(bob.address);389    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});390391    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;392    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;393    console.log(394      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',395      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),396    );397    console.log(398      '[Relay (Westend) -> Unique] transaction fees: %s WND',399      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),400    );401    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);402    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;403    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;404  });405406  itSub('Should connect and send Relay token back', async ({helper}) => {407    let relayTokenBalanceBefore: bigint;408    let relayTokenBalanceAfter: bigint;409    await usingRelayPlaygrounds(relayUrl, async (helper) => {410      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);411    });412413    const destination = {414      V2: {415        parents: 1,416        interior: {417          X1:{418            AccountId32: {419              network: 'Any',420              id: bob.addressRaw,421            },422          },423        },424      },425    };426427    const currencies: any = [428      [429        {430          NativeAssetId: 'Parent',431        },432        TRANSFER_AMOUNT_RELAY,433      ],434    ];435436    const feeItem = 0;437438    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');439440    balanceBobFinal = await helper.balance.getSubstrate(bob.address);441    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));442443    await usingRelayPlaygrounds(relayUrl, async (helper) => {444      await helper.wait.newBlocks(10);445      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);446447      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;448      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));449      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;450    });451  });452});453454describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {455  let alice: IKeyringPair;456  let randomAccount: IKeyringPair;457458  let balanceUniqueTokenInit: bigint;459  let balanceUniqueTokenMiddle: bigint;460  let balanceUniqueTokenFinal: bigint;461  let balanceAcalaTokenInit: bigint;462  let balanceAcalaTokenMiddle: bigint;463  let balanceAcalaTokenFinal: bigint;464  let balanceUniqueForeignTokenInit: bigint;465  let balanceUniqueForeignTokenMiddle: bigint;466  let balanceUniqueForeignTokenFinal: bigint;467468  // computed by a test transfer from prod Unique to prod Acala.469  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48470  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)471  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;472473  const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;474475  before(async () => {476    await usingPlaygrounds(async (helper, privateKey) => {477      alice = await privateKey('//Alice');478      [randomAccount] = await helper.arrange.createAccounts([0n], alice);479480      // Set the default version to wrap the first message to other chains.481      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);482    });483484    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {485      const destination = {486        V1: {487          parents: 1,488          interior: {489            X1: {490              Parachain: UNIQUE_CHAIN,491            },492          },493        },494      };495496      const metadata = {497        name: 'Unique Network',498        symbol: 'UNQ',499        decimals: 18,500        minimalBalance: 1250000000000000000n,501      };502503      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);504      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);505      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);506      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});507    });508509    await usingPlaygrounds(async (helper) => {510      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);511      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);512    });513  });514515  itSub('Should connect and send UNQ to Acala', async ({helper}) => {516517    const destination = {518      V2: {519        parents: 1,520        interior: {521          X1: {522            Parachain: ACALA_CHAIN,523          },524        },525      },526    };527528    const beneficiary = {529      V2: {530        parents: 0,531        interior: {532          X1: {533            AccountId32: {534              network: 'Any',535              id: randomAccount.addressRaw,536            },537          },538        },539      },540    };541542    const assets = {543      V2: [544        {545          id: {546            Concrete: {547              parents: 0,548              interior: 'Here',549            },550          },551          fun: {552            Fungible: TRANSFER_AMOUNT,553          },554        },555      ],556    };557558    const feeAssetItem = 0;559560    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');561    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);562563    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;564    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));565    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;566567    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {568      await helper.wait.newBlocks(3);569570      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});571      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);572573      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;574      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;575      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;576577      console.log(578        '[Unique -> Acala] transaction fees on Acala: %s ACA',579        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),580      );581      console.log(582        '[Unique -> Acala] transaction fees on Acala: %s UNQ',583        helper.util.bigIntToDecimals(acaUnqFees),584      );585      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));586      expect(acaFees == 0n).to.be.true;587      expect(588        acaUnqFees == expectedAcalaIncomeFee,589        'Acala took different income fee, check the Acala foreign asset config',590      ).to.be.true;591    });592  });593594  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {595    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {596      const destination = {597        V1: {598          parents: 1,599          interior: {600            X2: [601              {Parachain: UNIQUE_CHAIN},602              {603                AccountId32: {604                  network: 'Any',605                  id: randomAccount.addressRaw,606                },607              },608            ],609          },610        },611      };612613      const id = {614        ForeignAsset: 0,615      };616617      await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');618      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);619      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);620621      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;622      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;623624      console.log(625        '[Acala -> Unique] transaction fees on Acala: %s ACA',626        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),627      );628      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));629630      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;631      expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;632    });633634    await helper.wait.newBlocks(3);635636    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);637    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;638    expect(actuallyDelivered > 0).to.be.true;639640    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));641642    const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;643    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));644    expect(unqFees == 0n).to.be.true;645  });646647  itSub('Acala can send only up to its balance', async ({helper}) => {648    // set Acala's sovereign account's balance649    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);650    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);651    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);652653    const moreThanAcalaHas = acalaBalance * 2n;654655    let targetAccountBalance = 0n;656    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);657658    const uniqueMultilocation = {659      V1: {660        parents: 1,661        interior: {662          X1: {Parachain: UNIQUE_CHAIN},663        },664      },665    };666667    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(668      targetAccount.addressRaw,669      {670        Concrete: {671          parents: 0,672          interior: 'Here',673        },674      },675      moreThanAcalaHas,676    );677678    // Try to trick Unique679    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {680      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);681    });682683    const maxWaitBlocks = 3;684    const outcomeField = 1;685686    const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(687      maxWaitBlocks,688      'xcmpQueue',689      'Fail',690      outcomeField,691    );692693    expect(694      xcmpQueueFailEvent != null,695      '\'xcmpQueue.FailEvent\' event is expected',696    ).to.be.true;697698    expect(699      xcmpQueueFailEvent!.isFailedToTransactAsset,700      'The XCM error should be \'FailedToTransactAsset\'',701    ).to.be.true;702703    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);704    expect(targetAccountBalance).to.be.equal(0n);705706    // But Acala still can send the correct amount707    const validTransferAmount = acalaBalance / 2n;708    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(709      targetAccount.addressRaw,710      {711        Concrete: {712          parents: 0,713          interior: 'Here',714        },715      },716      validTransferAmount,717    );718719    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {720      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);721    });722723    await helper.wait.newBlocks(maxWaitBlocks);724725    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);726    expect(targetAccountBalance).to.be.equal(validTransferAmount);727  });728729  itSub.only('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {730    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);731    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);732733    const uniqueMultilocation = {734      V1: {735        parents: 1,736        interior: {737          X1: {738            Parachain: UNIQUE_CHAIN,739          },740        },741      },742    };743744    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(745      targetAccount.addressRaw,746      {747        Concrete: {748          parents: 1,749          interior: {750            X1: {751              Parachain: UNIQUE_CHAIN,752            },753          },754        },755      },756      testAmount,757    );758759    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(760      targetAccount.addressRaw,761      {762        Concrete: {763          parents: 0,764          interior: 'Here',765        },766      },767      testAmount,768    );769770    // Try to trick Unique using full UNQ identification771    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {772      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);773    });774775    const maxWaitBlocks = 3;776    const outcomeField = 1;777778    let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(779      maxWaitBlocks,780      'xcmpQueue',781      'Fail',782      outcomeField,783    );784785    expect(786      xcmpQueueFailEvent != null,787      '\'xcmpQueue.FailEvent\' event is expected',788    ).to.be.true;789790    expect(791      xcmpQueueFailEvent!.isUntrustedReserveLocation,792      'The XCM error should be \'isUntrustedReserveLocation\'',793    ).to.be.true;794795    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);796    expect(accountBalance).to.be.equal(0n);797798    // Try to trick Unique using shortened UNQ identification799    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {800      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);801    });802803    xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(804      maxWaitBlocks,805      'xcmpQueue',806      'Fail',807      outcomeField,808    );809810    expect(811      xcmpQueueFailEvent != null,812      '\'xcmpQueue.FailEvent\' event is expected',813    ).to.be.true;814815    expect(816      xcmpQueueFailEvent!.isUntrustedReserveLocation,817      'The XCM error should be \'isUntrustedReserveLocation\'',818    ).to.be.true;819820    accountBalance = await helper.balance.getSubstrate(targetAccount.address);821    expect(accountBalance).to.be.equal(0n);822  });823});824825// These tests are relevant only when826// the the corresponding foreign assets are not registered827describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {828  let alice: IKeyringPair;829  let alith: IKeyringPair;830831  const testAmount = 100_000_000_000n;832  let uniqueParachainJunction;833  let uniqueAccountJunction;834835  let uniqueParachainMultilocation: any;836  let uniqueAccountMultilocation: any;837  let uniqueCombinedMultilocation: any;838839  before(async () => {840    await usingPlaygrounds(async (helper, privateKey) => {841      alice = await privateKey('//Alice');842843      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};844      uniqueAccountJunction = {845        AccountId32: {846          network: 'Any',847          id: alice.addressRaw,848        },849      };850851      uniqueParachainMultilocation = {852        V1: {853          parents: 1,854          interior: {855            X1: uniqueParachainJunction,856          },857        },858      };859860      uniqueAccountMultilocation = {861        V1: {862          parents: 0,863          interior: {864            X1: uniqueAccountJunction,865          },866        },867      };868869      uniqueCombinedMultilocation = {870        V1: {871          parents: 1,872          interior: {873            X2: [uniqueParachainJunction, uniqueAccountJunction],874          },875        },876      };877878      // Set the default version to wrap the first message to other chains.879      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);880    });881882    // eslint-disable-next-line require-await883    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {884      alith = helper.account.alithAccount();885    });886  });887888  const expectFailedToTransact = async (network: string, helper: DevUniqueHelper) => {889    const maxWaitBlocks = 3;890    const outcomeField = 1;891892    const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(893      maxWaitBlocks,894      'xcmpQueue',895      'Fail',896      outcomeField,897    );898899    expect(900      xcmpQueueFailEvent != null,901      `[reject ${network} tokens] 'xcmpQueue.FailEvent' event is expected`,902    ).to.be.true;903904    expect(905      xcmpQueueFailEvent!.isFailedToTransactAsset,906      `[reject ${network} tokens] The XCM error should be 'FailedToTransactAsset'`,907    ).to.be.true;908  };909910  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {911    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {912      const id = {913        Token: 'ACA',914      };915      const destination = uniqueCombinedMultilocation;916      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');917    });918919    await expectFailedToTransact('ACA', helper);920  });921922  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {923    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {924      const id = 'SelfReserve';925      const destination = uniqueCombinedMultilocation;926      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');927    });928929    await expectFailedToTransact('GLMR', helper);930  });931932  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {933    await usingAstarPlaygrounds(astarUrl, async (helper) => {934      const destinationParachain = uniqueParachainMultilocation;935      const beneficiary = uniqueAccountMultilocation;936      const assets = {937        V1: [{938          id: {939            Concrete: {940              parents: 0,941              interior: 'Here',942            },943          },944          fun: {945            Fungible: testAmount,946          },947        }],948      };949      const feeAssetItem = 0;950951      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [952        destinationParachain,953        beneficiary,954        assets,955        feeAssetItem,956      ]);957    });958959    await expectFailedToTransact('ASTR', helper);960  });961});962963describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {964  // Unique constants965  let alice: IKeyringPair;966  let uniqueAssetLocation;967968  let randomAccountUnique: IKeyringPair;969  let randomAccountMoonbeam: IKeyringPair;970971  // Moonbeam constants972  let assetId: string;973974  const uniqueAssetMetadata = {975    name: 'xcUnique',976    symbol: 'xcUNQ',977    decimals: 18,978    isFrozen: false,979    minimalBalance: 1n,980  };981982  let balanceUniqueTokenInit: bigint;983  let balanceUniqueTokenMiddle: bigint;984  let balanceUniqueTokenFinal: bigint;985  let balanceForeignUnqTokenInit: bigint;986  let balanceForeignUnqTokenMiddle: bigint;987  let balanceForeignUnqTokenFinal: bigint;988  let balanceGlmrTokenInit: bigint;989  let balanceGlmrTokenMiddle: bigint;990  let balanceGlmrTokenFinal: bigint;991992  before(async () => {993    await usingPlaygrounds(async (helper, privateKey) => {994      alice = await privateKey('//Alice');995      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);996997      balanceForeignUnqTokenInit = 0n;998999      // Set the default version to wrap the first message to other chains.1000      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1001    });10021003    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1004      const alithAccount = helper.account.alithAccount();1005      const baltatharAccount = helper.account.baltatharAccount();1006      const dorothyAccount = helper.account.dorothyAccount();10071008      randomAccountMoonbeam = helper.account.create();10091010      // >>> Sponsoring Dorothy >>>1011      console.log('Sponsoring Dorothy.......');1012      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1013      console.log('Sponsoring Dorothy.......DONE');1014      // <<< Sponsoring Dorothy <<<10151016      uniqueAssetLocation = {1017        XCM: {1018          parents: 1,1019          interior: {X1: {Parachain: UNIQUE_CHAIN}},1020        },1021      };1022      const existentialDeposit = 1n;1023      const isSufficient = true;1024      const unitsPerSecond = 1n;1025      const numAssetsWeightHint = 0;10261027      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1028        location: uniqueAssetLocation,1029        metadata: uniqueAssetMetadata,1030        existentialDeposit,1031        isSufficient,1032        unitsPerSecond,1033        numAssetsWeightHint,1034      });10351036      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);10371038      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);10391040      // >>> Acquire Unique AssetId Info on Moonbeam >>>1041      console.log('Acquire Unique AssetId Info on Moonbeam.......');10421043      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();10441045      console.log('UNQ asset ID is %s', assetId);1046      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1047      // >>> Acquire Unique AssetId Info on Moonbeam >>>10481049      // >>> Sponsoring random Account >>>1050      console.log('Sponsoring random Account.......');1051      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1052      console.log('Sponsoring random Account.......DONE');1053      // <<< Sponsoring random Account <<<10541055      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1056    });10571058    await usingPlaygrounds(async (helper) => {1059      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1060      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1061    });1062  });10631064  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1065    const currencyId = {1066      NativeAssetId: 'Here',1067    };1068    const dest = {1069      V2: {1070        parents: 1,1071        interior: {1072          X2: [1073            {Parachain: MOONBEAM_CHAIN},1074            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1075          ],1076        },1077      },1078    };1079    const amount = TRANSFER_AMOUNT;10801081    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');10821083    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1084    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;10851086    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1087    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1088    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;10891090    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1091      await helper.wait.newBlocks(3);10921093      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);10941095      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1096      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1097      expect(glmrFees == 0n).to.be.true;10981099      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;11001101      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1102      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1103      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1104    });1105  });11061107  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1108    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1109      const asset = {1110        V1: {1111          id: {1112            Concrete: {1113              parents: 1,1114              interior: {1115                X1: {Parachain: UNIQUE_CHAIN},1116              },1117            },1118          },1119          fun: {1120            Fungible: TRANSFER_AMOUNT,1121          },1122        },1123      };1124      const destination = {1125        V1: {1126          parents: 1,1127          interior: {1128            X2: [1129              {Parachain: UNIQUE_CHAIN},1130              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1131            ],1132          },1133        },1134      };11351136      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');11371138      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);11391140      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1141      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1142      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;11431144      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);11451146      expect(unqRandomAccountAsset).to.be.null;11471148      balanceForeignUnqTokenFinal = 0n;11491150      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1151      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1152      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1153    });11541155    await helper.wait.newBlocks(3);11561157    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1158    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1159    expect(actuallyDelivered > 0).to.be.true;11601161    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));11621163    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1164    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1165    expect(unqFees == 0n).to.be.true;1166  });11671168  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1169    // set Moonbeam's sovereign account's balance1170    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1171    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1172    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);11731174    const moreThanMoonbeamHas = moonbeamBalance * 2n;11751176    let targetAccountBalance = 0n;1177    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11781179    const uniqueMultilocation = {1180      V1: {1181        parents: 1,1182        interior: {1183          X1: {Parachain: UNIQUE_CHAIN},1184        },1185      },1186    };11871188    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1189      targetAccount.addressRaw,1190      {1191        Concrete: {1192          parents: 0,1193          interior: 'Here',1194        },1195      },1196      moreThanMoonbeamHas,1197    );11981199    // Try to trick Unique1200    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1201      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);12021203      // Needed to bypass the call filter.1204      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1205      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);1206    });12071208    const maxWaitBlocks = 3;1209    const outcomeField = 1;12101211    const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(1212      maxWaitBlocks,1213      'xcmpQueue',1214      'Fail',1215      outcomeField,1216    );12171218    expect(1219      xcmpQueueFailEvent != null,1220      '\'xcmpQueue.FailEvent\' event is expected',1221    ).to.be.true;12221223    expect(1224      xcmpQueueFailEvent!.isFailedToTransactAsset,1225      'The XCM error should be \'FailedToTransactAsset\'',1226    ).to.be.true;12271228    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1229    expect(targetAccountBalance).to.be.equal(0n);12301231    // But Moonbeam still can send the correct amount1232    const validTransferAmount = moonbeamBalance / 2n;1233    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1234      targetAccount.addressRaw,1235      {1236        Concrete: {1237          parents: 0,1238          interior: 'Here',1239        },1240      },1241      validTransferAmount,1242    );12431244    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1245      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);12461247      // Needed to bypass the call filter.1248      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1249      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1250    });12511252    await helper.wait.newBlocks(maxWaitBlocks);12531254    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1255    expect(targetAccountBalance).to.be.equal(validTransferAmount);1256  });12571258  itSub.only('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1259    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1260    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12611262    const uniqueMultilocation = {1263      V1: {1264        parents: 1,1265        interior: {1266          X1: {1267            Parachain: UNIQUE_CHAIN,1268          },1269        },1270      },1271    };12721273    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1274      targetAccount.addressRaw,1275      {1276        Concrete: {1277          parents: 0,1278          interior: {1279            X1: {1280              Parachain: UNIQUE_CHAIN,1281            },1282          },1283        },1284      },1285      testAmount,1286    );12871288    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1289      targetAccount.addressRaw,1290      {1291        Concrete: {1292          parents: 0,1293          interior: 'Here',1294        },1295      },1296      testAmount,1297    );12981299    // Try to trick Unique using full UNQ identification1300    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1301      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);13021303      // Needed to bypass the call filter.1304      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1305      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);1306    });13071308    const maxWaitBlocks = 3;1309    const outcomeField = 1;13101311    let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(1312      maxWaitBlocks,1313      'xcmpQueue',1314      'Fail',1315      outcomeField,1316    );13171318    expect(1319      xcmpQueueFailEvent != null,1320      '\'xcmpQueue.FailEvent\' event is expected',1321    ).to.be.true;13221323    expect(1324      xcmpQueueFailEvent!.isUntrustedReserveLocation,1325      'The XCM error should be \'isUntrustedReserveLocation\'',1326    ).to.be.true;13271328    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1329    expect(accountBalance).to.be.equal(0n);13301331    // Try to trick Unique using shortened UNQ identification1332    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1333      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);13341335      // Needed to bypass the call filter.1336      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1337      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);1338    });13391340    xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(1341      maxWaitBlocks,1342      'xcmpQueue',1343      'Fail',1344      outcomeField,1345    );13461347    expect(1348      xcmpQueueFailEvent != null,1349      '\'xcmpQueue.FailEvent\' event is expected',1350    ).to.be.true;13511352    expect(1353      xcmpQueueFailEvent!.isUntrustedReserveLocation,1354      'The XCM error should be \'isUntrustedReserveLocation\'',1355    ).to.be.true;13561357    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1358    expect(accountBalance).to.be.equal(0n);1359  });1360});13611362describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1363  let alice: IKeyringPair;1364  let randomAccount: IKeyringPair;13651366  const UNQ_ASSET_ID_ON_ASTAR = 1;1367  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;13681369  // Unique -> Astar1370  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1371  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1372  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1373  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens13741375  // Astar -> Unique1376  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1377  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ13781379  let balanceAfterUniqueToAstarXCM: bigint;13801381  before(async () => {1382    await usingPlaygrounds(async (helper, privateKey) => {1383      alice = await privateKey('//Alice');1384      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1385      console.log('randomAccount', randomAccount.address);13861387      // Set the default version to wrap the first message to other chains.1388      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1389    });13901391    await usingAstarPlaygrounds(astarUrl, async (helper) => {1392      console.log('1. Create foreign asset and metadata');1393      // TODO update metadata with values from production1394      await helper.assets.create(1395        alice,1396        UNQ_ASSET_ID_ON_ASTAR,1397        alice.address,1398        UNQ_MINIMAL_BALANCE_ON_ASTAR,1399      );14001401      await helper.assets.setMetadata(1402        alice,1403        UNQ_ASSET_ID_ON_ASTAR,1404        'Cross chain UNQ',1405        'xcUNQ',1406        Number(UNQ_DECIMALS),1407      );14081409      console.log('2. Register asset location on Astar');1410      const assetLocation = {1411        V1: {1412          parents: 1,1413          interior: {1414            X1: {1415              Parachain: UNIQUE_CHAIN,1416            },1417          },1418        },1419      };14201421      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);14221423      console.log('3. Set UNQ payment for XCM execution on Astar');1424      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);14251426      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1427      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1428    });1429  });14301431  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1432    const destination = {1433      V2: {1434        parents: 1,1435        interior: {1436          X1: {1437            Parachain: ASTAR_CHAIN,1438          },1439        },1440      },1441    };14421443    const beneficiary = {1444      V2: {1445        parents: 0,1446        interior: {1447          X1: {1448            AccountId32: {1449              network: 'Any',1450              id: randomAccount.addressRaw,1451            },1452          },1453        },1454      },1455    };14561457    const assets = {1458      V2: [1459        {1460          id: {1461            Concrete: {1462              parents: 0,1463              interior: 'Here',1464            },1465          },1466          fun: {1467            Fungible: unqToAstarTransferred,1468          },1469        },1470      ],1471    };14721473    // Initial balance is 100 UNQ1474    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1475    console.log(`Initial balance is: ${balanceBefore}`);14761477    const feeAssetItem = 0;1478    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14791480    // Balance after reserve transfer is less than 901481    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1482    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1483    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1484    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;14851486    await usingAstarPlaygrounds(astarUrl, async (helper) => {1487      await helper.wait.newBlocks(3);1488      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1489      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);14901491      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1492      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);14931494      expect(xcUNQbalance).to.eq(unqToAstarArrived);1495      // Astar balance does not changed1496      expect(astarBalance).to.eq(astarInitialBalance);1497    });1498  });14991500  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1501    await usingAstarPlaygrounds(astarUrl, async (helper) => {1502      const destination = {1503        V1: {1504          parents: 1,1505          interior: {1506            X1: {1507              Parachain: UNIQUE_CHAIN,1508            },1509          },1510        },1511      };15121513      const beneficiary = {1514        V1: {1515          parents: 0,1516          interior: {1517            X1: {1518              AccountId32: {1519                network: 'Any',1520                id: randomAccount.addressRaw,1521              },1522            },1523          },1524        },1525      };15261527      const assets = {1528        V1: [1529          {1530            id: {1531              Concrete: {1532                parents: 1,1533                interior: {1534                  X1: {1535                    Parachain: UNIQUE_CHAIN,1536                  },1537                },1538              },1539            },1540            fun: {1541              Fungible: unqFromAstarTransfered,1542            },1543          },1544        ],1545      };15461547      // Initial balance is 1 ASTR1548      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1549      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1550      expect(balanceASTRbefore).to.eq(astarInitialBalance);15511552      const feeAssetItem = 0;1553      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1554      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);15551556      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1557      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1558      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);15591560      // Assert: xcUNQ balance correctly decreased1561      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1562      // Assert: ASTR balance is 0.996...1563      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1564    });15651566    await helper.wait.newBlocks(3);1567    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1568    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1569    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1570  });15711572  itSub('Astar can send only up to its balance', async ({helper}) => {1573    // set Astar's sovereign account's balance1574    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1575    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1576    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);15771578    const moreThanAstarHas = astarBalance * 2n;15791580    let targetAccountBalance = 0n;1581    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15821583    const uniqueMultilocation = {1584      V1: {1585        parents: 1,1586        interior: {1587          X1: {Parachain: UNIQUE_CHAIN},1588        },1589      },1590    };15911592    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1593      targetAccount.addressRaw,1594      {1595        Concrete: {1596          parents: 0,1597          interior: 'Here',1598        },1599      },1600      moreThanAstarHas,1601    );16021603    // Try to trick Unique1604    await usingAstarPlaygrounds(astarUrl, async (helper) => {1605      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);1606    });16071608    const maxWaitBlocks = 3;1609    const outcomeField = 1;16101611    const xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(1612      maxWaitBlocks,1613      'xcmpQueue',1614      'Fail',1615      outcomeField,1616    );16171618    expect(1619      xcmpQueueFailEvent != null,1620      '\'xcmpQueue.FailEvent\' event is expected',1621    ).to.be.true;16221623    expect(1624      xcmpQueueFailEvent!.isFailedToTransactAsset,1625      'The XCM error should be \'FailedToTransactAsset\'',1626    ).to.be.true;16271628    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1629    expect(targetAccountBalance).to.be.equal(0n);16301631    // But Astar still can send the correct amount1632    const validTransferAmount = astarBalance / 2n;1633    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1634      targetAccount.addressRaw,1635      {1636        Concrete: {1637          parents: 0,1638          interior: 'Here',1639        },1640      },1641      validTransferAmount,1642    );16431644    await usingAstarPlaygrounds(astarUrl, async (helper) => {1645      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1646    });16471648    await helper.wait.newBlocks(maxWaitBlocks);16491650    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1651    expect(targetAccountBalance).to.be.equal(validTransferAmount);1652  });16531654  itSub.only('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1655    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1656    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);16571658    const uniqueMultilocation = {1659      V1: {1660        parents: 1,1661        interior: {1662          X1: {1663            Parachain: UNIQUE_CHAIN,1664          },1665        },1666      },1667    };16681669    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1670      targetAccount.addressRaw,1671      {1672        Concrete: {1673          parents: 1,1674          interior: {1675            X1: {1676              Parachain: UNIQUE_CHAIN,1677            },1678          },1679        },1680      },1681      testAmount,1682    );16831684    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1685      targetAccount.addressRaw,1686      {1687        Concrete: {1688          parents: 0,1689          interior: 'Here',1690        },1691      },1692      testAmount,1693    );16941695    // Try to trick Unique using full UNQ identification1696    await usingAstarPlaygrounds(astarUrl, async (helper) => {1697      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);1698    });16991700    const maxWaitBlocks = 3;1701    const outcomeField = 1;17021703    let xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(1704      maxWaitBlocks,1705      'xcmpQueue',1706      'Fail',1707      outcomeField,1708    );17091710    expect(1711      xcmpQueueFailEvent != null,1712      '\'xcmpQueue.FailEvent\' event is expected',1713    ).to.be.true;17141715    expect(1716      xcmpQueueFailEvent!.isUntrustedReserveLocation,1717      'The XCM error should be \'isUntrustedReserveLocation\'',1718    ).to.be.true;17191720    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1721    expect(accountBalance).to.be.equal(0n);17221723    // Try to trick Unique using shortened UNQ identification1724    await usingAstarPlaygrounds(astarUrl, async (helper) => {1725      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);1726    });17271728    xcmpQueueFailEvent = await helper.wait.eventData<XcmV2TraitsError>(1729      maxWaitBlocks,1730      'xcmpQueue',1731      'Fail',1732      outcomeField,1733    );17341735    expect(1736      xcmpQueueFailEvent != null,1737      '\'xcmpQueue.FailEvent\' event is expected',1738    ).to.be.true;17391740    expect(1741      xcmpQueueFailEvent!.isUntrustedReserveLocation,1742      'The XCM error should be \'isUntrustedReserveLocation\'',1743    ).to.be.true;17441745    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1746    expect(accountBalance).to.be.equal(0n);1747  });17481749});