git.delta.rocks / unique-network / refs/commits / 7dbcac3e71d5

difftreelog

doc(polkadex CI): added comment

PraetorP2023-09-11parent: #9d3aabb.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 {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';21import {nToBigInt} from '@polkadot/util';2223const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);24const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);25const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);26const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);27const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);28const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);2930const STATEMINT_PALLET_INSTANCE = 50;3132const relayUrl = config.relayUrl;33const statemintUrl = config.statemintUrl;34const acalaUrl = config.acalaUrl;35const moonbeamUrl = config.moonbeamUrl;36const astarUrl = config.astarUrl;37const polkadexUrl = config.polkadexUrl;3839const RELAY_DECIMALS = 12;40const STATEMINT_DECIMALS = 12;41const ACALA_DECIMALS = 12;42const ASTAR_DECIMALS = 18n;43const UNQ_DECIMALS = 18n;4445const TRANSFER_AMOUNT = 2000000000000000000000000n;4647const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4849const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;5051const USDT_ASSET_ID = 100;52const USDT_ASSET_METADATA_DECIMALS = 18;53const USDT_ASSET_METADATA_NAME = 'USDT';54const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';55const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;56const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5758const SAFE_XCM_VERSION = 2;59const maxWaitBlocks = 6;60const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {61  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash62        && event.outcome.isFailedToTransactAsset);63};64const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {65  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash66         && event.outcome.isUntrustedReserveLocation);67};68describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {69  let alice: IKeyringPair;70  let bob: IKeyringPair;7172  let balanceStmnBefore: bigint;73  let balanceStmnAfter: bigint;7475  let balanceUniqueBefore: bigint;76  let balanceUniqueAfter: bigint;77  let balanceUniqueFinal: bigint;7879  let balanceBobBefore: bigint;80  let balanceBobAfter: bigint;81  let balanceBobFinal: bigint;8283  let balanceBobRelayTokenBefore: bigint;84  let balanceBobRelayTokenAfter: bigint;858687  before(async () => {88    await usingPlaygrounds(async (helper, privateKey) => {89      alice = await privateKey('//Alice');90      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor9192      // Set the default version to wrap the first message to other chains.93      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);94    });9596    await usingRelayPlaygrounds(relayUrl, async (helper) => {97      // Fund accounts on Statemint98      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);99      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);100    });101102    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {103      const sovereignFundingAmount = 3_500_000_000n;104105      await helper.assets.create(106        alice,107        USDT_ASSET_ID,108        alice.address,109        USDT_ASSET_METADATA_MINIMAL_BALANCE,110      );111      await helper.assets.setMetadata(112        alice,113        USDT_ASSET_ID,114        USDT_ASSET_METADATA_NAME,115        USDT_ASSET_METADATA_DESCRIPTION,116        USDT_ASSET_METADATA_DECIMALS,117      );118      await helper.assets.mint(119        alice,120        USDT_ASSET_ID,121        alice.address,122        USDT_ASSET_AMOUNT,123      );124125      // funding parachain sovereing account on Statemint.126      // The sovereign account should be created before any action127      // (the assets pallet on Statemint check if the sovereign account exists)128      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);129      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);130    });131132133    await usingPlaygrounds(async (helper) => {134      const location = {135        V2: {136          parents: 1,137          interior: {X3: [138            {139              Parachain: STATEMINT_CHAIN,140            },141            {142              PalletInstance: STATEMINT_PALLET_INSTANCE,143            },144            {145              GeneralIndex: USDT_ASSET_ID,146            },147          ]},148        },149      };150151      const metadata =152      {153        name: USDT_ASSET_ID,154        symbol: USDT_ASSET_METADATA_NAME,155        decimals: USDT_ASSET_METADATA_DECIMALS,156        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,157      };158      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);159      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);160    });161162163    // Providing the relay currency to the unique sender account164    // (fee for USDT XCM are paid in relay tokens)165    await usingRelayPlaygrounds(relayUrl, async (helper) => {166      const destination = {167        V2: {168          parents: 0,169          interior: {X1: {170            Parachain: UNIQUE_CHAIN,171          },172          },173        }};174175      const beneficiary = {176        V2: {177          parents: 0,178          interior: {X1: {179            AccountId32: {180              network: 'Any',181              id: alice.addressRaw,182            },183          }},184        },185      };186187      const assets = {188        V2: [189          {190            id: {191              Concrete: {192                parents: 0,193                interior: 'Here',194              },195            },196            fun: {197              Fungible: TRANSFER_AMOUNT_RELAY,198            },199          },200        ],201      };202203      const feeAssetItem = 0;204205      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');206    });207208  });209210  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {211    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {212      const dest = {213        V2: {214          parents: 1,215          interior: {X1: {216            Parachain: UNIQUE_CHAIN,217          },218          },219        }};220221      const beneficiary = {222        V2: {223          parents: 0,224          interior: {X1: {225            AccountId32: {226              network: 'Any',227              id: alice.addressRaw,228            },229          }},230        },231      };232233      const assets = {234        V2: [235          {236            id: {237              Concrete: {238                parents: 0,239                interior: {240                  X2: [241                    {242                      PalletInstance: STATEMINT_PALLET_INSTANCE,243                    },244                    {245                      GeneralIndex: USDT_ASSET_ID,246                    },247                  ]},248              },249            },250            fun: {251              Fungible: TRANSFER_AMOUNT,252            },253          },254        ],255      };256257      const feeAssetItem = 0;258259      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);260      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');261262      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);263264      // common good parachain take commission in it native token265      console.log(266        '[Statemint -> Unique] transaction fees on Statemint: %s WND',267        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),268      );269      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;270271    });272273274    // ensure that asset has been delivered275    await helper.wait.newBlocks(3);276277    // expext collection id will be with id 1278    const free = await helper.ft.getBalance(1, {Substrate: alice.address});279280    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);281282    console.log(283      '[Statemint -> Unique] transaction fees on Unique: %s USDT',284      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),285    );286    console.log(287      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',288      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),289    );290    // commission has not paid in USDT token291    expect(free).to.be.equal(TRANSFER_AMOUNT);292    // ... and parachain native token293    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;294  });295296  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {297    const destination = {298      V2: {299        parents: 1,300        interior: {X2: [301          {302            Parachain: STATEMINT_CHAIN,303          },304          {305            AccountId32: {306              network: 'Any',307              id: alice.addressRaw,308            },309          },310        ]},311      },312    };313314    const relayFee = 400_000_000_000_000n;315    const currencies: [any, bigint][] = [316      [317        {318          ForeignAssetId: 0,319        },320        TRANSFER_AMOUNT,321      ],322      [323        {324          NativeAssetId: 'Parent',325        },326        relayFee,327      ],328    ];329330    const feeItem = 1;331332    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');333334    // the commission has been paid in parachain native token335    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);336    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));337    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;338339    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {340      await helper.wait.newBlocks(3);341342      // The USDT token never paid fees. Its amount not changed from begin value.343      // Also check that xcm transfer has been succeeded344      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;345    });346  });347348  itSub('Should connect and send Relay token to Unique', async ({helper}) => {349    balanceBobBefore = await helper.balance.getSubstrate(bob.address);350    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});351352    await usingRelayPlaygrounds(relayUrl, async (helper) => {353      const destination = {354        V2: {355          parents: 0,356          interior: {X1: {357            Parachain: UNIQUE_CHAIN,358          },359          },360        }};361362      const beneficiary = {363        V2: {364          parents: 0,365          interior: {X1: {366            AccountId32: {367              network: 'Any',368              id: bob.addressRaw,369            },370          }},371        },372      };373374      const assets = {375        V2: [376          {377            id: {378              Concrete: {379                parents: 0,380                interior: 'Here',381              },382            },383            fun: {384              Fungible: TRANSFER_AMOUNT_RELAY,385            },386          },387        ],388      };389390      const feeAssetItem = 0;391392      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');393    });394395    await helper.wait.newBlocks(3);396397    balanceBobAfter = await helper.balance.getSubstrate(bob.address);398    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});399400    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;401    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;402    console.log(403      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',404      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),405    );406    console.log(407      '[Relay (Westend) -> Unique] transaction fees: %s WND',408      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),409    );410    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);411    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;412    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;413  });414415  itSub('Should connect and send Relay token back', async ({helper}) => {416    let relayTokenBalanceBefore: bigint;417    let relayTokenBalanceAfter: bigint;418    await usingRelayPlaygrounds(relayUrl, async (helper) => {419      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);420    });421422    const destination = {423      V2: {424        parents: 1,425        interior: {426          X1:{427            AccountId32: {428              network: 'Any',429              id: bob.addressRaw,430            },431          },432        },433      },434    };435436    const currencies: any = [437      [438        {439          NativeAssetId: 'Parent',440        },441        TRANSFER_AMOUNT_RELAY,442      ],443    ];444445    const feeItem = 0;446447    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');448449    balanceBobFinal = await helper.balance.getSubstrate(bob.address);450    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));451452    await usingRelayPlaygrounds(relayUrl, async (helper) => {453      await helper.wait.newBlocks(10);454      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);455456      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;457      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));458      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;459    });460  });461});462463describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {464  let alice: IKeyringPair;465  let randomAccount: IKeyringPair;466467  let balanceUniqueTokenInit: bigint;468  let balanceUniqueTokenMiddle: bigint;469  let balanceUniqueTokenFinal: bigint;470  let balanceAcalaTokenInit: bigint;471  let balanceAcalaTokenMiddle: bigint;472  let balanceAcalaTokenFinal: bigint;473  let balanceUniqueForeignTokenInit: bigint;474  let balanceUniqueForeignTokenMiddle: bigint;475  let balanceUniqueForeignTokenFinal: bigint;476477  // computed by a test transfer from prod Unique to prod Acala.478  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48479  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)480  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;481  const acalaEps = 8n * 10n ** 16n;482483  let acalaBackwardTransferAmount: bigint;484485  before(async () => {486    await usingPlaygrounds(async (helper, privateKey) => {487      alice = await privateKey('//Alice');488      [randomAccount] = await helper.arrange.createAccounts([0n], alice);489490      // Set the default version to wrap the first message to other chains.491      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);492    });493494    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {495      const destination = {496        V2: {497          parents: 1,498          interior: {499            X1: {500              Parachain: UNIQUE_CHAIN,501            },502          },503        },504      };505506      const metadata = {507        name: 'Unique Network',508        symbol: 'UNQ',509        decimals: 18,510        minimalBalance: 1250000000000000000n,511      };512513      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);514      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);515      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);516      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});517    });518519    await usingPlaygrounds(async (helper) => {520      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);521      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);522    });523  });524525  itSub('Should connect and send UNQ to Acala', async ({helper}) => {526527    const destination = {528      V2: {529        parents: 1,530        interior: {531          X1: {532            Parachain: ACALA_CHAIN,533          },534        },535      },536    };537538    const beneficiary = {539      V2: {540        parents: 0,541        interior: {542          X1: {543            AccountId32: {544              network: 'Any',545              id: randomAccount.addressRaw,546            },547          },548        },549      },550    };551552    const assets = {553      V2: [554        {555          id: {556            Concrete: {557              parents: 0,558              interior: 'Here',559            },560          },561          fun: {562            Fungible: TRANSFER_AMOUNT,563          },564        },565      ],566    };567568    const feeAssetItem = 0;569570    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');571    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);572573    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;574    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));575    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;576577    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {578      await helper.wait.newBlocks(3);579580      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});581      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);582583      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;584      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;585      acalaBackwardTransferAmount = unqIncomeTransfer;586587      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;588589      console.log(590        '[Unique -> Acala] transaction fees on Acala: %s ACA',591        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),592      );593      console.log(594        '[Unique -> Acala] transaction fees on Acala: %s UNQ',595        helper.util.bigIntToDecimals(acaUnqFees),596      );597      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));598      expect(acaFees == 0n).to.be.true;599600      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;601602      expect(603        bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,604        'Acala took different income fee, check the Acala foreign asset config',605      ).to.be.true;606    });607  });608609  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {610    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {611      const destination = {612        V2: {613          parents: 1,614          interior: {615            X2: [616              {Parachain: UNIQUE_CHAIN},617              {618                AccountId32: {619                  network: 'Any',620                  id: randomAccount.addressRaw,621                },622              },623            ],624          },625        },626      };627628      const id = {629        ForeignAsset: 0,630      };631632      await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');633      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);634      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);635636      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;637      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;638639      console.log(640        '[Acala -> Unique] transaction fees on Acala: %s ACA',641        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),642      );643      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));644645      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;646      expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;647    });648649    await helper.wait.newBlocks(3);650651    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);652    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;653    expect(actuallyDelivered > 0).to.be.true;654655    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));656657    const unqFees = acalaBackwardTransferAmount - actuallyDelivered;658    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));659    expect(unqFees == 0n).to.be.true;660  });661662  itSub('Acala can send only up to its balance', async ({helper}) => {663    // set Acala's sovereign account's balance664    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);665    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);666    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);667668    const moreThanAcalaHas = acalaBalance * 2n;669670    let targetAccountBalance = 0n;671    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);672673    const uniqueMultilocation = {674      V2: {675        parents: 1,676        interior: {677          X1: {Parachain: UNIQUE_CHAIN},678        },679      },680    };681682    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(683      targetAccount.addressRaw,684      {685        Concrete: {686          parents: 0,687          interior: 'Here',688        },689      },690      moreThanAcalaHas,691    );692693    let maliciousXcmProgramSent: any;694    const maxWaitBlocks = 3;695696    // Try to trick Unique697    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {698      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);699700      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);701    });702703    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash704        && event.outcome.isFailedToTransactAsset);705706    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);707    expect(targetAccountBalance).to.be.equal(0n);708709    // But Acala still can send the correct amount710    const validTransferAmount = acalaBalance / 2n;711    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(712      targetAccount.addressRaw,713      {714        Concrete: {715          parents: 0,716          interior: 'Here',717        },718      },719      validTransferAmount,720    );721722    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {723      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);724    });725726    await helper.wait.newBlocks(maxWaitBlocks);727728    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);729    expect(targetAccountBalance).to.be.equal(validTransferAmount);730  });731732  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {733    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);734    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);735736    const uniqueMultilocation = {737      V2: {738        parents: 1,739        interior: {740          X1: {741            Parachain: UNIQUE_CHAIN,742          },743        },744      },745    };746747    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(748      targetAccount.addressRaw,749      {750        Concrete: {751          parents: 1,752          interior: {753            X1: {754              Parachain: UNIQUE_CHAIN,755            },756          },757        },758      },759      testAmount,760    );761762    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(763      targetAccount.addressRaw,764      {765        Concrete: {766          parents: 0,767          interior: 'Here',768        },769      },770      testAmount,771    );772773    let maliciousXcmProgramFullIdSent: any;774    let maliciousXcmProgramHereIdSent: any;775    const maxWaitBlocks = 3;776777    // Try to trick Unique using full UNQ identification778    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {779      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);780781      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);782    });783784    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash785        && event.outcome.isUntrustedReserveLocation);786787    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);788    expect(accountBalance).to.be.equal(0n);789790    // Try to trick Unique using shortened UNQ identification791    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {792      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);793794      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);795    });796797    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash798        && event.outcome.isUntrustedReserveLocation);799800    accountBalance = await helper.balance.getSubstrate(targetAccount.address);801    expect(accountBalance).to.be.equal(0n);802  });803});804805describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {806  let alice: IKeyringPair;807  let randomAccount: IKeyringPair;808  let unqFees: bigint;809  let balanceUniqueTokenInit: bigint;810  let balanceUniqueTokenMiddle: bigint;811  let balanceUniqueTokenFinal: bigint;812  const maxWaitBlocks = 6;813814  const uniqueAssetId = {815    Concrete: {816      parents: 1,817      interior: {818        X1: {819          Parachain: UNIQUE_CHAIN,820        },821      },822    },823  };824825  before(async () => {826    await usingPlaygrounds(async (helper, privateKey) => {827      alice = await privateKey('//Alice');828      [randomAccount] = await helper.arrange.createAccounts([0n], alice);829830      // Set the default version to wrap the first message to other chains.831      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);832    });833834    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {835      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])).toJSON() as [])836        .map(nToBigInt).length != 0;837838      if(!isWhitelisted) {839        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);840      }841842      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);843    });844845    await usingPlaygrounds(async (helper) => {846      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);847      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);848    });849  });850851  itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {852853    const destination = {854      V2: {855        parents: 1,856        interior: {857          X1: {858            Parachain: POLKADEX_CHAIN,859          },860        },861      },862    };863864    const beneficiary = {865      V2: {866        parents: 0,867        interior: {868          X1: {869            AccountId32: {870              network: 'Any',871              id: randomAccount.addressRaw,872            },873          },874        },875      },876    };877878    const assets = {879      V2: [880        {881          id: {882            Concrete: {883              parents: 0,884              interior: 'Here',885            },886          },887          fun: {888            Fungible: TRANSFER_AMOUNT,889          },890        },891      ],892    };893894    const feeAssetItem = 0;895896    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');897    const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);898    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);899900    unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;901    console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));902    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;903904    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {905      /*906        Since only the parachain part of the Polkadex907        infrastructure is launched (without their908        solochain validators), processing incoming909        assets will lead to an error.910        This error indicates that the Polkadex chain911        received a message from the Unique network,912        since the hash is being checked to ensure913        it matches what was sent.914      */915      await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);916    });917  });918919920  itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {921922    const uniqueMultilocation = {923      V2: {924        parents: 1,925        interior: {926          X1: {Parachain: UNIQUE_CHAIN},927        },928      },929    };930931    const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(932      randomAccount.addressRaw,933      {934        Concrete: {935          parents: 1,936          interior: {937            X1: {Parachain: UNIQUE_CHAIN},938          },939        },940      },941      TRANSFER_AMOUNT,942    );943944    let xcmProgramSent: any;945946947    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {948      await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);949950      xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);951    });952953    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);954955    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);956957    expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);958  });959960  itSub('Polkadex can send only up to its balance', async ({helper}) => {961    const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);962    const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);963    await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);964    const moreThanPolkadexHas = 2n * polkadexBalance;965966    const targetAccount = helper.arrange.createEmptyAccount();967968    const uniqueMultilocation = {969      V2: {970        parents: 1,971        interior: {972          X1: {Parachain: UNIQUE_CHAIN},973        },974      },975    };976977    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(978      targetAccount.addressRaw,979      {980        Concrete: {981          parents: 0,982          interior: 'Here',983        },984      },985      moreThanPolkadexHas,986    );987988    let maliciousXcmProgramSent: any;989990991    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {992      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);993994      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);995    });996997    await expectFailedToTransact(helper, maliciousXcmProgramSent);998999    const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1000    expect(targetAccountBalance).to.be.equal(0n);1001  });10021003  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {1004    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1005    const targetAccount = helper.arrange.createEmptyAccount();10061007    const uniqueMultilocation = {1008      V2: {1009        parents: 1,1010        interior: {1011          X1: {1012            Parachain: UNIQUE_CHAIN,1013          },1014        },1015      },1016    };10171018    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1019      targetAccount.addressRaw,1020      {1021        Concrete: {1022          parents: 1,1023          interior: {1024            X1: {1025              Parachain: UNIQUE_CHAIN,1026            },1027          },1028        },1029      },1030      testAmount,1031    );10321033    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1034      targetAccount.addressRaw,1035      {1036        Concrete: {1037          parents: 0,1038          interior: 'Here',1039        },1040      },1041      testAmount,1042    );10431044    let maliciousXcmProgramFullIdSent: any;1045    let maliciousXcmProgramHereIdSent: any;1046    const maxWaitBlocks = 3;10471048    // Try to trick Unique using full UNQ identification1049    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1050      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10511052      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1053    });10541055    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10561057    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1058    expect(accountBalance).to.be.equal(0n);10591060    // Try to trick Unique using shortened UNQ identification1061    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1062      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10631064      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1065    });10661067    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10681069    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1070    expect(accountBalance).to.be.equal(0n);1071  });1072});10731074// These tests are relevant only when1075// the the corresponding foreign assets are not registered1076describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1077  let alice: IKeyringPair;1078  let alith: IKeyringPair;10791080  const testAmount = 100_000_000_000n;1081  let uniqueParachainJunction;1082  let uniqueAccountJunction;10831084  let uniqueParachainMultilocation: any;1085  let uniqueAccountMultilocation: any;1086  let uniqueCombinedMultilocation: any;1087  let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V210881089  let messageSent: any;10901091  const maxWaitBlocks = 3;10921093  before(async () => {1094    await usingPlaygrounds(async (helper, privateKey) => {1095      alice = await privateKey('//Alice');10961097      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1098      uniqueAccountJunction = {1099        AccountId32: {1100          network: 'Any',1101          id: alice.addressRaw,1102        },1103      };11041105      uniqueParachainMultilocation = {1106        V2: {1107          parents: 1,1108          interior: {1109            X1: uniqueParachainJunction,1110          },1111        },1112      };11131114      uniqueAccountMultilocation = {1115        V2: {1116          parents: 0,1117          interior: {1118            X1: uniqueAccountJunction,1119          },1120        },1121      };11221123      uniqueCombinedMultilocation = {1124        V2: {1125          parents: 1,1126          interior: {1127            X2: [uniqueParachainJunction, uniqueAccountJunction],1128          },1129        },1130      };11311132      uniqueCombinedMultilocationAcala = {1133        V2: {1134          parents: 1,1135          interior: {1136            X2: [uniqueParachainJunction, uniqueAccountJunction],1137          },1138        },1139      };11401141      // Set the default version to wrap the first message to other chains.1142      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1143    });11441145    // eslint-disable-next-line require-await1146    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1147      alith = helper.account.alithAccount();1148    });1149  });1150115111521153  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1154    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1155      const id = {1156        Token: 'ACA',1157      };1158      const destination = uniqueCombinedMultilocationAcala;1159      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11601161      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1162    });11631164    await expectFailedToTransact(helper, messageSent);1165  });11661167  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1168    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1169      const id = 'SelfReserve';1170      const destination = uniqueCombinedMultilocation;1171      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11721173      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1174    });11751176    await expectFailedToTransact(helper, messageSent);1177  });11781179  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1180    await usingAstarPlaygrounds(astarUrl, async (helper) => {1181      const destinationParachain = uniqueParachainMultilocation;1182      const beneficiary = uniqueAccountMultilocation;1183      const assets = {1184        V2: [{1185          id: {1186            Concrete: {1187              parents: 0,1188              interior: 'Here',1189            },1190          },1191          fun: {1192            Fungible: testAmount,1193          },1194        }],1195      };1196      const feeAssetItem = 0;11971198      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1199        destinationParachain,1200        beneficiary,1201        assets,1202        feeAssetItem,1203      ]);12041205      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1206    });12071208    await expectFailedToTransact(helper, messageSent);1209  });12101211  itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12121213    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1214      helper.arrange.createEmptyAccount().addressRaw,1215      {1216        Concrete: {1217          parents: 1,1218          interior: {1219            X1: {1220              Parachain: POLKADEX_CHAIN,1221            },1222          },1223        },1224      },1225      testAmount,1226    );12271228    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1229      await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1230      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1231    });12321233    await expectFailedToTransact(helper, messageSent);1234  });1235});12361237describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1238  // Unique constants1239  let alice: IKeyringPair;1240  let uniqueAssetLocation;12411242  let randomAccountUnique: IKeyringPair;1243  let randomAccountMoonbeam: IKeyringPair;12441245  // Moonbeam constants1246  let assetId: string;12471248  const uniqueAssetMetadata = {1249    name: 'xcUnique',1250    symbol: 'xcUNQ',1251    decimals: 18,1252    isFrozen: false,1253    minimalBalance: 1n,1254  };12551256  let balanceUniqueTokenInit: bigint;1257  let balanceUniqueTokenMiddle: bigint;1258  let balanceUniqueTokenFinal: bigint;1259  let balanceForeignUnqTokenInit: bigint;1260  let balanceForeignUnqTokenMiddle: bigint;1261  let balanceForeignUnqTokenFinal: bigint;1262  let balanceGlmrTokenInit: bigint;1263  let balanceGlmrTokenMiddle: bigint;1264  let balanceGlmrTokenFinal: bigint;12651266  before(async () => {1267    await usingPlaygrounds(async (helper, privateKey) => {1268      alice = await privateKey('//Alice');1269      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12701271      balanceForeignUnqTokenInit = 0n;12721273      // Set the default version to wrap the first message to other chains.1274      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1275    });12761277    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1278      const alithAccount = helper.account.alithAccount();1279      const baltatharAccount = helper.account.baltatharAccount();1280      const dorothyAccount = helper.account.dorothyAccount();12811282      randomAccountMoonbeam = helper.account.create();12831284      // >>> Sponsoring Dorothy >>>1285      console.log('Sponsoring Dorothy.......');1286      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1287      console.log('Sponsoring Dorothy.......DONE');1288      // <<< Sponsoring Dorothy <<<12891290      uniqueAssetLocation = {1291        XCM: {1292          parents: 1,1293          interior: {X1: {Parachain: UNIQUE_CHAIN}},1294        },1295      };1296      const existentialDeposit = 1n;1297      const isSufficient = true;1298      const unitsPerSecond = 1n;1299      const numAssetsWeightHint = 0;13001301      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1302        location: uniqueAssetLocation,1303        metadata: uniqueAssetMetadata,1304        existentialDeposit,1305        isSufficient,1306        unitsPerSecond,1307        numAssetsWeightHint,1308      });13091310      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13111312      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13131314      // >>> Acquire Unique AssetId Info on Moonbeam >>>1315      console.log('Acquire Unique AssetId Info on Moonbeam.......');13161317      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13181319      console.log('UNQ asset ID is %s', assetId);1320      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1321      // >>> Acquire Unique AssetId Info on Moonbeam >>>13221323      // >>> Sponsoring random Account >>>1324      console.log('Sponsoring random Account.......');1325      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1326      console.log('Sponsoring random Account.......DONE');1327      // <<< Sponsoring random Account <<<13281329      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1330    });13311332    await usingPlaygrounds(async (helper) => {1333      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1334      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1335    });1336  });13371338  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1339    const currencyId = {1340      NativeAssetId: 'Here',1341    };1342    const dest = {1343      V2: {1344        parents: 1,1345        interior: {1346          X2: [1347            {Parachain: MOONBEAM_CHAIN},1348            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1349          ],1350        },1351      },1352    };1353    const amount = TRANSFER_AMOUNT;13541355    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13561357    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1358    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13591360    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1361    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1362    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13631364    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1365      await helper.wait.newBlocks(3);13661367      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13681369      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1370      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1371      expect(glmrFees == 0n).to.be.true;13721373      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13741375      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1376      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1377      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1378    });1379  });13801381  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1382    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1383      const asset = {1384        V2: {1385          id: {1386            Concrete: {1387              parents: 1,1388              interior: {1389                X1: {Parachain: UNIQUE_CHAIN},1390              },1391            },1392          },1393          fun: {1394            Fungible: TRANSFER_AMOUNT,1395          },1396        },1397      };1398      const destination = {1399        V2: {1400          parents: 1,1401          interior: {1402            X2: [1403              {Parachain: UNIQUE_CHAIN},1404              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1405            ],1406          },1407        },1408      };14091410      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14111412      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14131414      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1415      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1416      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14171418      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14191420      expect(unqRandomAccountAsset).to.be.null;14211422      balanceForeignUnqTokenFinal = 0n;14231424      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1425      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1426      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1427    });14281429    await helper.wait.newBlocks(3);14301431    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1432    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1433    expect(actuallyDelivered > 0).to.be.true;14341435    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14361437    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1438    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1439    expect(unqFees == 0n).to.be.true;1440  });14411442  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1443    // set Moonbeam's sovereign account's balance1444    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1445    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1446    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14471448    const moreThanMoonbeamHas = moonbeamBalance * 2n;14491450    let targetAccountBalance = 0n;1451    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14521453    const uniqueMultilocation = {1454      V2: {1455        parents: 1,1456        interior: {1457          X1: {Parachain: UNIQUE_CHAIN},1458        },1459      },1460    };14611462    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1463      targetAccount.addressRaw,1464      {1465        Concrete: {1466          parents: 0,1467          interior: 'Here',1468        },1469      },1470      moreThanMoonbeamHas,1471    );14721473    let maliciousXcmProgramSent: any;1474    const maxWaitBlocks = 3;14751476    // Try to trick Unique1477    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1478      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14791480      // Needed to bypass the call filter.1481      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1482      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14831484      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1485    });14861487    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1488        && event.outcome.isFailedToTransactAsset);14891490    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1491    expect(targetAccountBalance).to.be.equal(0n);14921493    // But Moonbeam still can send the correct amount1494    const validTransferAmount = moonbeamBalance / 2n;1495    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1496      targetAccount.addressRaw,1497      {1498        Concrete: {1499          parents: 0,1500          interior: 'Here',1501        },1502      },1503      validTransferAmount,1504    );15051506    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1507      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);15081509      // Needed to bypass the call filter.1510      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1511      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1512    });15131514    await helper.wait.newBlocks(maxWaitBlocks);15151516    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1517    expect(targetAccountBalance).to.be.equal(validTransferAmount);1518  });15191520  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1521    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1522    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15231524    const uniqueMultilocation = {1525      V2: {1526        parents: 1,1527        interior: {1528          X1: {1529            Parachain: UNIQUE_CHAIN,1530          },1531        },1532      },1533    };15341535    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1536      targetAccount.addressRaw,1537      {1538        Concrete: {1539          parents: 0,1540          interior: {1541            X1: {1542              Parachain: UNIQUE_CHAIN,1543            },1544          },1545        },1546      },1547      testAmount,1548    );15491550    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1551      targetAccount.addressRaw,1552      {1553        Concrete: {1554          parents: 0,1555          interior: 'Here',1556        },1557      },1558      testAmount,1559    );15601561    let maliciousXcmProgramFullIdSent: any;1562    let maliciousXcmProgramHereIdSent: any;1563    const maxWaitBlocks = 3;15641565    // Try to trick Unique using full UNQ identification1566    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1567      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15681569      // Needed to bypass the call filter.1570      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1571      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15721573      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1574    });15751576    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1577        && event.outcome.isUntrustedReserveLocation);15781579    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1580    expect(accountBalance).to.be.equal(0n);15811582    // Try to trick Unique using shortened UNQ identification1583    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1584      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15851586      // Needed to bypass the call filter.1587      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1588      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15891590      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1591    });15921593    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1594        && event.outcome.isUntrustedReserveLocation);15951596    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1597    expect(accountBalance).to.be.equal(0n);1598  });1599});16001601describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1602  let alice: IKeyringPair;1603  let randomAccount: IKeyringPair;16041605  const UNQ_ASSET_ID_ON_ASTAR = 1;1606  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;16071608  // Unique -> Astar1609  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1610  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1611  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1612  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens16131614  // Astar -> Unique1615  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1616  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ16171618  let balanceAfterUniqueToAstarXCM: bigint;16191620  before(async () => {1621    await usingPlaygrounds(async (helper, privateKey) => {1622      alice = await privateKey('//Alice');1623      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1624      console.log('randomAccount', randomAccount.address);16251626      // Set the default version to wrap the first message to other chains.1627      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1628    });16291630    await usingAstarPlaygrounds(astarUrl, async (helper) => {1631      console.log('1. Create foreign asset and metadata');1632      // TODO update metadata with values from production1633      await helper.assets.create(1634        alice,1635        UNQ_ASSET_ID_ON_ASTAR,1636        alice.address,1637        UNQ_MINIMAL_BALANCE_ON_ASTAR,1638      );16391640      await helper.assets.setMetadata(1641        alice,1642        UNQ_ASSET_ID_ON_ASTAR,1643        'Cross chain UNQ',1644        'xcUNQ',1645        Number(UNQ_DECIMALS),1646      );16471648      console.log('2. Register asset location on Astar');1649      const assetLocation = {1650        V2: {1651          parents: 1,1652          interior: {1653            X1: {1654              Parachain: UNIQUE_CHAIN,1655            },1656          },1657        },1658      };16591660      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16611662      console.log('3. Set UNQ payment for XCM execution on Astar');1663      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);16641665      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1666      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1667    });1668  });16691670  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1671    const destination = {1672      V2: {1673        parents: 1,1674        interior: {1675          X1: {1676            Parachain: ASTAR_CHAIN,1677          },1678        },1679      },1680    };16811682    const beneficiary = {1683      V2: {1684        parents: 0,1685        interior: {1686          X1: {1687            AccountId32: {1688              network: 'Any',1689              id: randomAccount.addressRaw,1690            },1691          },1692        },1693      },1694    };16951696    const assets = {1697      V2: [1698        {1699          id: {1700            Concrete: {1701              parents: 0,1702              interior: 'Here',1703            },1704          },1705          fun: {1706            Fungible: unqToAstarTransferred,1707          },1708        },1709      ],1710    };17111712    // Initial balance is 100 UNQ1713    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1714    console.log(`Initial balance is: ${balanceBefore}`);17151716    const feeAssetItem = 0;1717    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');17181719    // Balance after reserve transfer is less than 901720    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1721    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1722    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1723    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17241725    await usingAstarPlaygrounds(astarUrl, async (helper) => {1726      await helper.wait.newBlocks(3);1727      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1728      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17291730      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1731      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);17321733      expect(xcUNQbalance).to.eq(unqToAstarArrived);1734      // Astar balance does not changed1735      expect(astarBalance).to.eq(astarInitialBalance);1736    });1737  });17381739  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1740    await usingAstarPlaygrounds(astarUrl, async (helper) => {1741      const destination = {1742        V2: {1743          parents: 1,1744          interior: {1745            X1: {1746              Parachain: UNIQUE_CHAIN,1747            },1748          },1749        },1750      };17511752      const beneficiary = {1753        V2: {1754          parents: 0,1755          interior: {1756            X1: {1757              AccountId32: {1758                network: 'Any',1759                id: randomAccount.addressRaw,1760              },1761            },1762          },1763        },1764      };17651766      const assets = {1767        V2: [1768          {1769            id: {1770              Concrete: {1771                parents: 1,1772                interior: {1773                  X1: {1774                    Parachain: UNIQUE_CHAIN,1775                  },1776                },1777              },1778            },1779            fun: {1780              Fungible: unqFromAstarTransfered,1781            },1782          },1783        ],1784      };17851786      // Initial balance is 1 ASTR1787      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1788      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1789      expect(balanceASTRbefore).to.eq(astarInitialBalance);17901791      const feeAssetItem = 0;1792      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1793      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17941795      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1796      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1797      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17981799      // Assert: xcUNQ balance correctly decreased1800      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1801      // Assert: ASTR balance is 0.996...1802      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1803    });18041805    await helper.wait.newBlocks(3);1806    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1807    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1808    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1809  });18101811  itSub('Astar can send only up to its balance', async ({helper}) => {1812    // set Astar's sovereign account's balance1813    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1814    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1815    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);18161817    const moreThanAstarHas = astarBalance * 2n;18181819    let targetAccountBalance = 0n;1820    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18211822    const uniqueMultilocation = {1823      V2: {1824        parents: 1,1825        interior: {1826          X1: {Parachain: UNIQUE_CHAIN},1827        },1828      },1829    };18301831    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1832      targetAccount.addressRaw,1833      {1834        Concrete: {1835          parents: 0,1836          interior: 'Here',1837        },1838      },1839      moreThanAstarHas,1840    );18411842    let maliciousXcmProgramSent: any;1843    const maxWaitBlocks = 3;18441845    // Try to trick Unique1846    await usingAstarPlaygrounds(astarUrl, async (helper) => {1847      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18481849      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1850    });18511852    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1853        && event.outcome.isFailedToTransactAsset);18541855    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1856    expect(targetAccountBalance).to.be.equal(0n);18571858    // But Astar still can send the correct amount1859    const validTransferAmount = astarBalance / 2n;1860    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1861      targetAccount.addressRaw,1862      {1863        Concrete: {1864          parents: 0,1865          interior: 'Here',1866        },1867      },1868      validTransferAmount,1869    );18701871    await usingAstarPlaygrounds(astarUrl, async (helper) => {1872      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1873    });18741875    await helper.wait.newBlocks(maxWaitBlocks);18761877    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1878    expect(targetAccountBalance).to.be.equal(validTransferAmount);1879  });18801881  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1882    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1883    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18841885    const uniqueMultilocation = {1886      V2: {1887        parents: 1,1888        interior: {1889          X1: {1890            Parachain: UNIQUE_CHAIN,1891          },1892        },1893      },1894    };18951896    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1897      targetAccount.addressRaw,1898      {1899        Concrete: {1900          parents: 1,1901          interior: {1902            X1: {1903              Parachain: UNIQUE_CHAIN,1904            },1905          },1906        },1907      },1908      testAmount,1909    );19101911    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1912      targetAccount.addressRaw,1913      {1914        Concrete: {1915          parents: 0,1916          interior: 'Here',1917        },1918      },1919      testAmount,1920    );19211922    let maliciousXcmProgramFullIdSent: any;1923    let maliciousXcmProgramHereIdSent: any;1924    const maxWaitBlocks = 3;19251926    // Try to trick Unique using full UNQ identification1927    await usingAstarPlaygrounds(astarUrl, async (helper) => {1928      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);19291930      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1931    });19321933    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1934        && event.outcome.isUntrustedReserveLocation);19351936    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1937    expect(accountBalance).to.be.equal(0n);19381939    // Try to trick Unique using shortened UNQ identification1940    await usingAstarPlaygrounds(astarUrl, async (helper) => {1941      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19421943      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1944    });19451946    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1947        && event.outcome.isUntrustedReserveLocation);19481949    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1950    expect(accountBalance).to.be.equal(0n);1951  });1952});