git.delta.rocks / unique-network / refs/commits / 683a1283827b

difftreelog

test added CI for the Polkadex network

PraetorP2023-09-11parent: #2fcc0ad.patch.diff
in: master

7 files changed

addedtests/.mocha.envdiffbeforeafterboth
--- /dev/null
+++ b/tests/.mocha.env
@@ -0,0 +1,20 @@
+RELAY_ACALA_ID=1002
+RELAY_ASTAR_ID=1005
+RELAY_MOONBEAM_ID=1003
+RELAY_POLKADEX_ID=1006
+RELAY_STATEMINT_ID=1004
+RELAY_UNIQUE_ID=1001
+RELAY_HTTP_URL=http://127.0.0.1:9699/relay/
+RELAY_ACALA_HTTP_URL=http://127.0.0.1:9699/relay-acala/
+RELAY_ASTAR_HTTP_URL=http://127.0.0.1:9699/relay-astar/
+RELAY_MOONBEAM_HTTP_URL=http://127.0.0.1:9699/relay-moonbeam/
+RELAY_POLKADEX_HTTP_URL=http://127.0.0.1:9699/relay-polkadex/
+RELAY_STATEMINT_HTTP_URL=http://127.0.0.1:9699/relay-statemint/
+RELAY_UNIQUE_HTTP_URL=http://127.0.0.1:9699/relay-unique/
+RELAY_URL=ws://127.0.0.1:9699/relay/
+RELAY_ACALA_URL=ws://127.0.0.1:9699/relay-acala/
+RELAY_ASTAR_URL=ws://127.0.0.1:9699/relay-astar/
+RELAY_MOONBEAM_URL=ws://127.0.0.1:9699/relay-moonbeam/
+RELAY_POLKADEX_URL=ws://127.0.0.1:9699/relay-polkadex/
+RELAY_STATEMINT_URL=ws://127.0.0.1:9699/relay-statemint/
+RELAY_UNIQUE_URL=ws://127.0.0.1:9699/relay-unique/
modifiedtests/.vscode/settings.jsondiffbeforeafterboth
--- a/tests/.vscode/settings.json
+++ b/tests/.vscode/settings.json
@@ -1,6 +1,10 @@
 {
-	"mocha.enabled": true,
-	"mochaExplorer.files": "**/*.test.ts",
+	"mochaExplorer.env": {
+		"RUN_GOV_TESTS": "1",
+		"RUN_XCM_TESTS": "1"
+	},
+	"mochaExplorer.files": "src/**/*.test.ts",
+	"mochaExplorer.envPath": ".mocha.env",
 	"mochaExplorer.require": "ts-node/register",
 	"eslint.format.enable": true,
 	"[javascript]": {
modifiedtests/src/config.tsdiffbeforeafterboth
--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -28,6 +28,7 @@
   westmintUrl: process.env.RELAY_WESTMINT_URL || 'ws://127.0.0.1:9948',
   statemineUrl: process.env.RELAY_STATEMINE_URL || 'ws://127.0.0.1:9948',
   statemintUrl: process.env.RELAY_STATEMINT_URL || 'ws://127.0.0.1:9948',
+  polkadexUrl: process.env.RELAY_POLKADEX_URL || 'ws://127.0.0.1:9950',
 };
 
 export default config;
modifiedtests/src/util/index.tsdiffbeforeafterboth
--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -11,7 +11,7 @@
 import config from '../config';
 import {ChainHelperBase} from './playgrounds/unique';
 import {ILogger} from './playgrounds/types';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper} from './playgrounds/unique.dev';
+import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper} from './playgrounds/unique.dev';
 import {dirname} from 'path';
 import {fileURLToPath} from 'url';
 
@@ -89,6 +89,8 @@
 
 export const usingShidenPlaygrounds = (url: string, code: (helper: DevShidenHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevShidenHelper>(DevShidenHelper, url, code);
 
+export const usingPolkadexPlaygrounds = (url: string, code: (helper: DevPolkadexHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => usingPlaygroundsGeneral<DevPolkadexHelper>(DevPolkadexHelper, url, code);
+
 export const MINIMUM_DONOR_FUND = 4_000_000n;
 export const DONOR_FUNDING = 4_000_000n;
 
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -3,7 +3,7 @@
 
 import {stringToU8a} from '@polkadot/util';
 import {blake2AsHex, encodeAddress, mnemonicGenerate} from '@polkadot/util-crypto';
-import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper, AstarHelper} from './unique';
+import {UniqueHelper, MoonbeamHelper, ChainHelperBase, AcalaHelper, RelayHelper, WestmintHelper, AstarHelper, PolkadexHelper} from './unique';
 import {ApiPromise, Keyring, WsProvider} from '@polkadot/api';
 import * as defs from '../../interfaces/definitions';
 import {IKeyringPair} from '@polkadot/types/types';
@@ -249,6 +249,10 @@
       messageHash: eventJsonData(data, 0),
     }));
 
+    static Success = this.Method('Success', data => ({
+      messageHash: eventJsonData(data, 0),
+    }));
+
     static Fail = this.Method('Fail', data => ({
       messageHash: eventJsonData(data, 0),
       outcome: eventData<XcmV2TraitsError>(data, 1),
@@ -406,6 +410,16 @@
   }
 }
 
+export class DevPolkadexHelper extends PolkadexHelper {
+  wait: WaitGroup;
+  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+    options.helperBase = options.helperBase ?? PolkadexHelper;
+
+    super(logger, options);
+    this.wait = new WaitGroup(this);
+  }
+}
+
 export class DevKaruraHelper extends DevAcalaHelper {}
 
 export class ArrangeGroup {
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -3694,6 +3694,12 @@
   }
 }
 
+class PolkadexXcmHelperGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+  async whitelistToken(signer: TSigner, assetId: any) {
+    await this.helper.executeExtrinsic(signer, 'api.tx.xcmHelper.whitelistToken', [assetId], true);
+  }
+}
+
 class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
   async accounts(address: string, currencyId: any) {
     const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;
@@ -3977,6 +3983,30 @@
   }
 }
 
+export class PolkadexHelper extends XcmChainHelper {
+  assets: AssetsGroup<PolkadexHelper>;
+  balance: SubstrateBalanceGroup<PolkadexHelper>;
+  xTokens: XTokensGroup<PolkadexHelper>;
+  xcm: XcmGroup<PolkadexHelper>;
+  xcmHelper: PolkadexXcmHelperGroup<PolkadexHelper>;
+
+  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+    super(logger, options.helperBase ?? PolkadexHelper);
+
+    this.assets = new AssetsGroup(this);
+    this.balance = new SubstrateBalanceGroup(this);
+    this.xTokens = new XTokensGroup(this);
+    this.xcm = new XcmGroup(this, 'polkadotXcm');
+    this.xcmHelper = new PolkadexXcmHelperGroup(this);
+  }
+
+  getSudo<T extends PolkadexHelper>() {
+    // eslint-disable-next-line @typescript-eslint/naming-convention
+    const SudoHelperType = SudoHelper(this.helperBase);
+    return this.clone(SudoHelperType) as T;
+  }
+}
+
 // eslint-disable-next-line @typescript-eslint/naming-convention
 function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {
   return class extends Base {
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} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';2122const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);23const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);24const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);25const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);26const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);2728const STATEMINT_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemintUrl = config.statemintUrl;32const acalaUrl = config.acalaUrl;33const moonbeamUrl = config.moonbeamUrl;34const astarUrl = config.astarUrl;3536const RELAY_DECIMALS = 12;37const STATEMINT_DECIMALS = 12;38const ACALA_DECIMALS = 12;39const ASTAR_DECIMALS = 18n;40const UNQ_DECIMALS = 18n;4142const TRANSFER_AMOUNT = 2000000000000000000000000n;4344const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4546const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4748const USDT_ASSET_ID = 100;49const USDT_ASSET_METADATA_DECIMALS = 18;50const USDT_ASSET_METADATA_NAME = 'USDT';51const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';52const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;53const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5455const SAFE_XCM_VERSION = 2;5657describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {58  let alice: IKeyringPair;59  let bob: IKeyringPair;6061  let balanceStmnBefore: bigint;62  let balanceStmnAfter: bigint;6364  let balanceUniqueBefore: bigint;65  let balanceUniqueAfter: bigint;66  let balanceUniqueFinal: bigint;6768  let balanceBobBefore: bigint;69  let balanceBobAfter: bigint;70  let balanceBobFinal: bigint;7172  let balanceBobRelayTokenBefore: bigint;73  let balanceBobRelayTokenAfter: bigint;747576  before(async () => {77    await usingPlaygrounds(async (helper, privateKey) => {78      alice = await privateKey('//Alice');79      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor8081      // Set the default version to wrap the first message to other chains.82      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);83    });8485    await usingRelayPlaygrounds(relayUrl, async (helper) => {86      // Fund accounts on Statemint87      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);88      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);89    });9091    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {92      const sovereignFundingAmount = 3_500_000_000n;9394      await helper.assets.create(95        alice,96        USDT_ASSET_ID,97        alice.address,98        USDT_ASSET_METADATA_MINIMAL_BALANCE,99      );100      await helper.assets.setMetadata(101        alice,102        USDT_ASSET_ID,103        USDT_ASSET_METADATA_NAME,104        USDT_ASSET_METADATA_DESCRIPTION,105        USDT_ASSET_METADATA_DECIMALS,106      );107      await helper.assets.mint(108        alice,109        USDT_ASSET_ID,110        alice.address,111        USDT_ASSET_AMOUNT,112      );113114      // funding parachain sovereing account on Statemint.115      // The sovereign account should be created before any action116      // (the assets pallet on Statemint check if the sovereign account exists)117      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);118      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);119    });120121122    await usingPlaygrounds(async (helper) => {123      const location = {124        V2: {125          parents: 1,126          interior: {X3: [127            {128              Parachain: STATEMINT_CHAIN,129            },130            {131              PalletInstance: STATEMINT_PALLET_INSTANCE,132            },133            {134              GeneralIndex: USDT_ASSET_ID,135            },136          ]},137        },138      };139140      const metadata =141      {142        name: USDT_ASSET_ID,143        symbol: USDT_ASSET_METADATA_NAME,144        decimals: USDT_ASSET_METADATA_DECIMALS,145        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,146      };147      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);148      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);149    });150151152    // Providing the relay currency to the unique sender account153    // (fee for USDT XCM are paid in relay tokens)154    await usingRelayPlaygrounds(relayUrl, async (helper) => {155      const destination = {156        V2: {157          parents: 0,158          interior: {X1: {159            Parachain: UNIQUE_CHAIN,160          },161          },162        }};163164      const beneficiary = {165        V2: {166          parents: 0,167          interior: {X1: {168            AccountId32: {169              network: 'Any',170              id: alice.addressRaw,171            },172          }},173        },174      };175176      const assets = {177        V2: [178          {179            id: {180              Concrete: {181                parents: 0,182                interior: 'Here',183              },184            },185            fun: {186              Fungible: TRANSFER_AMOUNT_RELAY,187            },188          },189        ],190      };191192      const feeAssetItem = 0;193194      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');195    });196197  });198199  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {200    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {201      const dest = {202        V2: {203          parents: 1,204          interior: {X1: {205            Parachain: UNIQUE_CHAIN,206          },207          },208        }};209210      const beneficiary = {211        V2: {212          parents: 0,213          interior: {X1: {214            AccountId32: {215              network: 'Any',216              id: alice.addressRaw,217            },218          }},219        },220      };221222      const assets = {223        V2: [224          {225            id: {226              Concrete: {227                parents: 0,228                interior: {229                  X2: [230                    {231                      PalletInstance: STATEMINT_PALLET_INSTANCE,232                    },233                    {234                      GeneralIndex: USDT_ASSET_ID,235                    },236                  ]},237              },238            },239            fun: {240              Fungible: TRANSFER_AMOUNT,241            },242          },243        ],244      };245246      const feeAssetItem = 0;247248      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);249      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');250251      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);252253      // common good parachain take commission in it native token254      console.log(255        '[Statemint -> Unique] transaction fees on Statemint: %s WND',256        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),257      );258      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;259260    });261262263    // ensure that asset has been delivered264    await helper.wait.newBlocks(3);265266    // expext collection id will be with id 1267    const free = await helper.ft.getBalance(1, {Substrate: alice.address});268269    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);270271    console.log(272      '[Statemint -> Unique] transaction fees on Unique: %s USDT',273      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),274    );275    console.log(276      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',277      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),278    );279    // commission has not paid in USDT token280    expect(free).to.be.equal(TRANSFER_AMOUNT);281    // ... and parachain native token282    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;283  });284285  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {286    const destination = {287      V2: {288        parents: 1,289        interior: {X2: [290          {291            Parachain: STATEMINT_CHAIN,292          },293          {294            AccountId32: {295              network: 'Any',296              id: alice.addressRaw,297            },298          },299        ]},300      },301    };302303    const relayFee = 400_000_000_000_000n;304    const currencies: [any, bigint][] = [305      [306        {307          ForeignAssetId: 0,308        },309        TRANSFER_AMOUNT,310      ],311      [312        {313          NativeAssetId: 'Parent',314        },315        relayFee,316      ],317    ];318319    const feeItem = 1;320321    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');322323    // the commission has been paid in parachain native token324    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);325    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));326    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;327328    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {329      await helper.wait.newBlocks(3);330331      // The USDT token never paid fees. Its amount not changed from begin value.332      // Also check that xcm transfer has been succeeded333      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;334    });335  });336337  itSub('Should connect and send Relay token to Unique', async ({helper}) => {338    balanceBobBefore = await helper.balance.getSubstrate(bob.address);339    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});340341    await usingRelayPlaygrounds(relayUrl, async (helper) => {342      const destination = {343        V2: {344          parents: 0,345          interior: {X1: {346            Parachain: UNIQUE_CHAIN,347          },348          },349        }};350351      const beneficiary = {352        V2: {353          parents: 0,354          interior: {X1: {355            AccountId32: {356              network: 'Any',357              id: bob.addressRaw,358            },359          }},360        },361      };362363      const assets = {364        V2: [365          {366            id: {367              Concrete: {368                parents: 0,369                interior: 'Here',370              },371            },372            fun: {373              Fungible: TRANSFER_AMOUNT_RELAY,374            },375          },376        ],377      };378379      const feeAssetItem = 0;380381      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');382    });383384    await helper.wait.newBlocks(3);385386    balanceBobAfter = await helper.balance.getSubstrate(bob.address);387    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});388389    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;390    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;391    console.log(392      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',393      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),394    );395    console.log(396      '[Relay (Westend) -> Unique] transaction fees: %s WND',397      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),398    );399    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);400    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;401    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;402  });403404  itSub('Should connect and send Relay token back', async ({helper}) => {405    let relayTokenBalanceBefore: bigint;406    let relayTokenBalanceAfter: bigint;407    await usingRelayPlaygrounds(relayUrl, async (helper) => {408      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);409    });410411    const destination = {412      V2: {413        parents: 1,414        interior: {415          X1:{416            AccountId32: {417              network: 'Any',418              id: bob.addressRaw,419            },420          },421        },422      },423    };424425    const currencies: any = [426      [427        {428          NativeAssetId: 'Parent',429        },430        TRANSFER_AMOUNT_RELAY,431      ],432    ];433434    const feeItem = 0;435436    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');437438    balanceBobFinal = await helper.balance.getSubstrate(bob.address);439    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));440441    await usingRelayPlaygrounds(relayUrl, async (helper) => {442      await helper.wait.newBlocks(10);443      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);444445      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;446      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));447      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;448    });449  });450});451452describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {453  let alice: IKeyringPair;454  let randomAccount: IKeyringPair;455456  let balanceUniqueTokenInit: bigint;457  let balanceUniqueTokenMiddle: bigint;458  let balanceUniqueTokenFinal: bigint;459  let balanceAcalaTokenInit: bigint;460  let balanceAcalaTokenMiddle: bigint;461  let balanceAcalaTokenFinal: bigint;462  let balanceUniqueForeignTokenInit: bigint;463  let balanceUniqueForeignTokenMiddle: bigint;464  let balanceUniqueForeignTokenFinal: bigint;465466  // computed by a test transfer from prod Unique to prod Acala.467  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48468  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)469  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;470  const acalaEps = 8n * 10n ** 16n;471472  let acalaBackwardTransferAmount: bigint;473474  before(async () => {475    await usingPlaygrounds(async (helper, privateKey) => {476      alice = await privateKey('//Alice');477      [randomAccount] = await helper.arrange.createAccounts([0n], alice);478479      // Set the default version to wrap the first message to other chains.480      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);481    });482483    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {484      const destination = {485        V2: {486          parents: 1,487          interior: {488            X1: {489              Parachain: UNIQUE_CHAIN,490            },491          },492        },493      };494495      const metadata = {496        name: 'Unique Network',497        symbol: 'UNQ',498        decimals: 18,499        minimalBalance: 1250000000000000000n,500      };501502      await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);503      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);504      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);505      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});506    });507508    await usingPlaygrounds(async (helper) => {509      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);510      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);511    });512  });513514  itSub('Should connect and send UNQ to Acala', async ({helper}) => {515516    const destination = {517      V2: {518        parents: 1,519        interior: {520          X1: {521            Parachain: ACALA_CHAIN,522          },523        },524      },525    };526527    const beneficiary = {528      V2: {529        parents: 0,530        interior: {531          X1: {532            AccountId32: {533              network: 'Any',534              id: randomAccount.addressRaw,535            },536          },537        },538      },539    };540541    const assets = {542      V2: [543        {544          id: {545            Concrete: {546              parents: 0,547              interior: 'Here',548            },549          },550          fun: {551            Fungible: TRANSFER_AMOUNT,552          },553        },554      ],555    };556557    const feeAssetItem = 0;558559    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');560    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);561562    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;563    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));564    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;565566    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {567      await helper.wait.newBlocks(3);568569      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});570      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);571572      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;573      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;574      acalaBackwardTransferAmount = unqIncomeTransfer;575576      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;577578      console.log(579        '[Unique -> Acala] transaction fees on Acala: %s ACA',580        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),581      );582      console.log(583        '[Unique -> Acala] transaction fees on Acala: %s UNQ',584        helper.util.bigIntToDecimals(acaUnqFees),585      );586      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));587      expect(acaFees == 0n).to.be.true;588589      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;590591      expect(592        bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,593        'Acala took different income fee, check the Acala foreign asset config',594      ).to.be.true;595    });596  });597598  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {599    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {600      const destination = {601        V2: {602          parents: 1,603          interior: {604            X2: [605              {Parachain: UNIQUE_CHAIN},606              {607                AccountId32: {608                  network: 'Any',609                  id: randomAccount.addressRaw,610                },611              },612            ],613          },614        },615      };616617      const id = {618        ForeignAsset: 0,619      };620621      await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');622      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);623      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);624625      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;626      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;627628      console.log(629        '[Acala -> Unique] transaction fees on Acala: %s ACA',630        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),631      );632      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));633634      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;635      expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;636    });637638    await helper.wait.newBlocks(3);639640    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);641    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;642    expect(actuallyDelivered > 0).to.be.true;643644    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));645646    const unqFees = acalaBackwardTransferAmount - actuallyDelivered;647    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));648    expect(unqFees == 0n).to.be.true;649  });650651  itSub('Acala can send only up to its balance', async ({helper}) => {652    // set Acala's sovereign account's balance653    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);654    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);655    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);656657    const moreThanAcalaHas = acalaBalance * 2n;658659    let targetAccountBalance = 0n;660    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);661662    const uniqueMultilocation = {663      V2: {664        parents: 1,665        interior: {666          X1: {Parachain: UNIQUE_CHAIN},667        },668      },669    };670671    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(672      targetAccount.addressRaw,673      {674        Concrete: {675          parents: 0,676          interior: 'Here',677        },678      },679      moreThanAcalaHas,680    );681682    let maliciousXcmProgramSent: any;683    const maxWaitBlocks = 3;684685    // Try to trick Unique686    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {687      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);688689      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);690    });691692    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash693        && event.outcome.isFailedToTransactAsset);694695    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);696    expect(targetAccountBalance).to.be.equal(0n);697698    // But Acala still can send the correct amount699    const validTransferAmount = acalaBalance / 2n;700    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(701      targetAccount.addressRaw,702      {703        Concrete: {704          parents: 0,705          interior: 'Here',706        },707      },708      validTransferAmount,709    );710711    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {712      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);713    });714715    await helper.wait.newBlocks(maxWaitBlocks);716717    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);718    expect(targetAccountBalance).to.be.equal(validTransferAmount);719  });720721  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {722    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);723    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);724725    const uniqueMultilocation = {726      V2: {727        parents: 1,728        interior: {729          X1: {730            Parachain: UNIQUE_CHAIN,731          },732        },733      },734    };735736    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(737      targetAccount.addressRaw,738      {739        Concrete: {740          parents: 1,741          interior: {742            X1: {743              Parachain: UNIQUE_CHAIN,744            },745          },746        },747      },748      testAmount,749    );750751    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(752      targetAccount.addressRaw,753      {754        Concrete: {755          parents: 0,756          interior: 'Here',757        },758      },759      testAmount,760    );761762    let maliciousXcmProgramFullIdSent: any;763    let maliciousXcmProgramHereIdSent: any;764    const maxWaitBlocks = 3;765766    // Try to trick Unique using full UNQ identification767    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {768      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);769770      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);771    });772773    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash774        && event.outcome.isUntrustedReserveLocation);775776    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);777    expect(accountBalance).to.be.equal(0n);778779    // Try to trick Unique using shortened UNQ identification780    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {781      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);782783      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);784    });785786    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash787        && event.outcome.isUntrustedReserveLocation);788789    accountBalance = await helper.balance.getSubstrate(targetAccount.address);790    expect(accountBalance).to.be.equal(0n);791  });792});793794// These tests are relevant only when795// the the corresponding foreign assets are not registered796describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {797  let alice: IKeyringPair;798  let alith: IKeyringPair;799800  const testAmount = 100_000_000_000n;801  let uniqueParachainJunction;802  let uniqueAccountJunction;803804  let uniqueParachainMultilocation: any;805  let uniqueAccountMultilocation: any;806  let uniqueCombinedMultilocation: any;807  let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2808809  let messageSent: any;810811  const maxWaitBlocks = 3;812813  before(async () => {814    await usingPlaygrounds(async (helper, privateKey) => {815      alice = await privateKey('//Alice');816817      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};818      uniqueAccountJunction = {819        AccountId32: {820          network: 'Any',821          id: alice.addressRaw,822        },823      };824825      uniqueParachainMultilocation = {826        V2: {827          parents: 1,828          interior: {829            X1: uniqueParachainJunction,830          },831        },832      };833834      uniqueAccountMultilocation = {835        V2: {836          parents: 0,837          interior: {838            X1: uniqueAccountJunction,839          },840        },841      };842843      uniqueCombinedMultilocation = {844        V2: {845          parents: 1,846          interior: {847            X2: [uniqueParachainJunction, uniqueAccountJunction],848          },849        },850      };851852      uniqueCombinedMultilocationAcala = {853        V2: {854          parents: 1,855          interior: {856            X2: [uniqueParachainJunction, uniqueAccountJunction],857          },858        },859      };860861      // Set the default version to wrap the first message to other chains.862      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);863    });864865    // eslint-disable-next-line require-await866    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {867      alith = helper.account.alithAccount();868    });869  });870871  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {872    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash873        && event.outcome.isFailedToTransactAsset);874  };875876  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {877    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {878      const id = {879        Token: 'ACA',880      };881      const destination = uniqueCombinedMultilocationAcala;882      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');883884      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);885    });886887    await expectFailedToTransact(helper, messageSent);888  });889890  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {891    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {892      const id = 'SelfReserve';893      const destination = uniqueCombinedMultilocation;894      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');895896      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);897    });898899    await expectFailedToTransact(helper, messageSent);900  });901902  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {903    await usingAstarPlaygrounds(astarUrl, async (helper) => {904      const destinationParachain = uniqueParachainMultilocation;905      const beneficiary = uniqueAccountMultilocation;906      const assets = {907        V2: [{908          id: {909            Concrete: {910              parents: 0,911              interior: 'Here',912            },913          },914          fun: {915            Fungible: testAmount,916          },917        }],918      };919      const feeAssetItem = 0;920921      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [922        destinationParachain,923        beneficiary,924        assets,925        feeAssetItem,926      ]);927928      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);929    });930931    await expectFailedToTransact(helper, messageSent);932  });933});934935describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {936  // Unique constants937  let alice: IKeyringPair;938  let uniqueAssetLocation;939940  let randomAccountUnique: IKeyringPair;941  let randomAccountMoonbeam: IKeyringPair;942943  // Moonbeam constants944  let assetId: string;945946  const uniqueAssetMetadata = {947    name: 'xcUnique',948    symbol: 'xcUNQ',949    decimals: 18,950    isFrozen: false,951    minimalBalance: 1n,952  };953954  let balanceUniqueTokenInit: bigint;955  let balanceUniqueTokenMiddle: bigint;956  let balanceUniqueTokenFinal: bigint;957  let balanceForeignUnqTokenInit: bigint;958  let balanceForeignUnqTokenMiddle: bigint;959  let balanceForeignUnqTokenFinal: bigint;960  let balanceGlmrTokenInit: bigint;961  let balanceGlmrTokenMiddle: bigint;962  let balanceGlmrTokenFinal: bigint;963964  before(async () => {965    await usingPlaygrounds(async (helper, privateKey) => {966      alice = await privateKey('//Alice');967      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);968969      balanceForeignUnqTokenInit = 0n;970971      // Set the default version to wrap the first message to other chains.972      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);973    });974975    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {976      const alithAccount = helper.account.alithAccount();977      const baltatharAccount = helper.account.baltatharAccount();978      const dorothyAccount = helper.account.dorothyAccount();979980      randomAccountMoonbeam = helper.account.create();981982      // >>> Sponsoring Dorothy >>>983      console.log('Sponsoring Dorothy.......');984      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);985      console.log('Sponsoring Dorothy.......DONE');986      // <<< Sponsoring Dorothy <<<987988      uniqueAssetLocation = {989        XCM: {990          parents: 1,991          interior: {X1: {Parachain: UNIQUE_CHAIN}},992        },993      };994      const existentialDeposit = 1n;995      const isSufficient = true;996      const unitsPerSecond = 1n;997      const numAssetsWeightHint = 0;998999      const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1000        location: uniqueAssetLocation,1001        metadata: uniqueAssetMetadata,1002        existentialDeposit,1003        isSufficient,1004        unitsPerSecond,1005        numAssetsWeightHint,1006      });10071008      console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);10091010      await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);10111012      // >>> Acquire Unique AssetId Info on Moonbeam >>>1013      console.log('Acquire Unique AssetId Info on Moonbeam.......');10141015      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();10161017      console.log('UNQ asset ID is %s', assetId);1018      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1019      // >>> Acquire Unique AssetId Info on Moonbeam >>>10201021      // >>> Sponsoring random Account >>>1022      console.log('Sponsoring random Account.......');1023      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1024      console.log('Sponsoring random Account.......DONE');1025      // <<< Sponsoring random Account <<<10261027      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1028    });10291030    await usingPlaygrounds(async (helper) => {1031      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1032      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1033    });1034  });10351036  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1037    const currencyId = {1038      NativeAssetId: 'Here',1039    };1040    const dest = {1041      V2: {1042        parents: 1,1043        interior: {1044          X2: [1045            {Parachain: MOONBEAM_CHAIN},1046            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1047          ],1048        },1049      },1050    };1051    const amount = TRANSFER_AMOUNT;10521053    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');10541055    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1056    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;10571058    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1059    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1060    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;10611062    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1063      await helper.wait.newBlocks(3);10641065      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);10661067      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1068      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1069      expect(glmrFees == 0n).to.be.true;10701071      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;10721073      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1074      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1075      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1076    });1077  });10781079  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1080    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1081      const asset = {1082        V2: {1083          id: {1084            Concrete: {1085              parents: 1,1086              interior: {1087                X1: {Parachain: UNIQUE_CHAIN},1088              },1089            },1090          },1091          fun: {1092            Fungible: TRANSFER_AMOUNT,1093          },1094        },1095      };1096      const destination = {1097        V2: {1098          parents: 1,1099          interior: {1100            X2: [1101              {Parachain: UNIQUE_CHAIN},1102              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1103            ],1104          },1105        },1106      };11071108      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');11091110      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);11111112      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1113      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1114      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;11151116      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);11171118      expect(unqRandomAccountAsset).to.be.null;11191120      balanceForeignUnqTokenFinal = 0n;11211122      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1123      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1124      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1125    });11261127    await helper.wait.newBlocks(3);11281129    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1130    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1131    expect(actuallyDelivered > 0).to.be.true;11321133    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));11341135    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1136    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1137    expect(unqFees == 0n).to.be.true;1138  });11391140  itSub('Moonbeam can send only up to its balance', async ({helper}) => {1141    // set Moonbeam's sovereign account's balance1142    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1143    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1144    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);11451146    const moreThanMoonbeamHas = moonbeamBalance * 2n;11471148    let targetAccountBalance = 0n;1149    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11501151    const uniqueMultilocation = {1152      V2: {1153        parents: 1,1154        interior: {1155          X1: {Parachain: UNIQUE_CHAIN},1156        },1157      },1158    };11591160    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1161      targetAccount.addressRaw,1162      {1163        Concrete: {1164          parents: 0,1165          interior: 'Here',1166        },1167      },1168      moreThanMoonbeamHas,1169    );11701171    let maliciousXcmProgramSent: any;1172    const maxWaitBlocks = 3;11731174    // Try to trick Unique1175    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1176      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);11771178      // Needed to bypass the call filter.1179      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1180      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);11811182      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1183    });11841185    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1186        && event.outcome.isFailedToTransactAsset);11871188    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1189    expect(targetAccountBalance).to.be.equal(0n);11901191    // But Moonbeam still can send the correct amount1192    const validTransferAmount = moonbeamBalance / 2n;1193    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1194      targetAccount.addressRaw,1195      {1196        Concrete: {1197          parents: 0,1198          interior: 'Here',1199        },1200      },1201      validTransferAmount,1202    );12031204    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1205      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);12061207      // Needed to bypass the call filter.1208      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1209      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1210    });12111212    await helper.wait.newBlocks(maxWaitBlocks);12131214    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1215    expect(targetAccountBalance).to.be.equal(validTransferAmount);1216  });12171218  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1219    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1220    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12211222    const uniqueMultilocation = {1223      V2: {1224        parents: 1,1225        interior: {1226          X1: {1227            Parachain: UNIQUE_CHAIN,1228          },1229        },1230      },1231    };12321233    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1234      targetAccount.addressRaw,1235      {1236        Concrete: {1237          parents: 0,1238          interior: {1239            X1: {1240              Parachain: UNIQUE_CHAIN,1241            },1242          },1243        },1244      },1245      testAmount,1246    );12471248    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1249      targetAccount.addressRaw,1250      {1251        Concrete: {1252          parents: 0,1253          interior: 'Here',1254        },1255      },1256      testAmount,1257    );12581259    let maliciousXcmProgramFullIdSent: any;1260    let maliciousXcmProgramHereIdSent: any;1261    const maxWaitBlocks = 3;12621263    // Try to trick Unique using full UNQ identification1264    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1265      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);12661267      // Needed to bypass the call filter.1268      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1269      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);12701271      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1272    });12731274    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1275        && event.outcome.isUntrustedReserveLocation);12761277    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1278    expect(accountBalance).to.be.equal(0n);12791280    // Try to trick Unique using shortened UNQ identification1281    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1282      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);12831284      // Needed to bypass the call filter.1285      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1286      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);12871288      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1289    });12901291    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1292        && event.outcome.isUntrustedReserveLocation);12931294    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1295    expect(accountBalance).to.be.equal(0n);1296  });1297});12981299describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1300  let alice: IKeyringPair;1301  let randomAccount: IKeyringPair;13021303  const UNQ_ASSET_ID_ON_ASTAR = 1;1304  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;13051306  // Unique -> Astar1307  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1308  const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1309  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1310  const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens13111312  // Astar -> Unique1313  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1314  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ13151316  let balanceAfterUniqueToAstarXCM: bigint;13171318  before(async () => {1319    await usingPlaygrounds(async (helper, privateKey) => {1320      alice = await privateKey('//Alice');1321      [randomAccount] = await helper.arrange.createAccounts([100n], alice);1322      console.log('randomAccount', randomAccount.address);13231324      // Set the default version to wrap the first message to other chains.1325      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1326    });13271328    await usingAstarPlaygrounds(astarUrl, async (helper) => {1329      console.log('1. Create foreign asset and metadata');1330      // TODO update metadata with values from production1331      await helper.assets.create(1332        alice,1333        UNQ_ASSET_ID_ON_ASTAR,1334        alice.address,1335        UNQ_MINIMAL_BALANCE_ON_ASTAR,1336      );13371338      await helper.assets.setMetadata(1339        alice,1340        UNQ_ASSET_ID_ON_ASTAR,1341        'Cross chain UNQ',1342        'xcUNQ',1343        Number(UNQ_DECIMALS),1344      );13451346      console.log('2. Register asset location on Astar');1347      const assetLocation = {1348        V2: {1349          parents: 1,1350          interior: {1351            X1: {1352              Parachain: UNIQUE_CHAIN,1353            },1354          },1355        },1356      };13571358      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);13591360      console.log('3. Set UNQ payment for XCM execution on Astar');1361      await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);13621363      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1364      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1365    });1366  });13671368  itSub('Should connect and send UNQ to Astar', async ({helper}) => {1369    const destination = {1370      V2: {1371        parents: 1,1372        interior: {1373          X1: {1374            Parachain: ASTAR_CHAIN,1375          },1376        },1377      },1378    };13791380    const beneficiary = {1381      V2: {1382        parents: 0,1383        interior: {1384          X1: {1385            AccountId32: {1386              network: 'Any',1387              id: randomAccount.addressRaw,1388            },1389          },1390        },1391      },1392    };13931394    const assets = {1395      V2: [1396        {1397          id: {1398            Concrete: {1399              parents: 0,1400              interior: 'Here',1401            },1402          },1403          fun: {1404            Fungible: unqToAstarTransferred,1405          },1406        },1407      ],1408    };14091410    // Initial balance is 100 UNQ1411    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1412    console.log(`Initial balance is: ${balanceBefore}`);14131414    const feeAssetItem = 0;1415    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14161417    // Balance after reserve transfer is less than 901418    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1419    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1420    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1421    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;14221423    await usingAstarPlaygrounds(astarUrl, async (helper) => {1424      await helper.wait.newBlocks(3);1425      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1426      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);14271428      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1429      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);14301431      expect(xcUNQbalance).to.eq(unqToAstarArrived);1432      // Astar balance does not changed1433      expect(astarBalance).to.eq(astarInitialBalance);1434    });1435  });14361437  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1438    await usingAstarPlaygrounds(astarUrl, async (helper) => {1439      const destination = {1440        V2: {1441          parents: 1,1442          interior: {1443            X1: {1444              Parachain: UNIQUE_CHAIN,1445            },1446          },1447        },1448      };14491450      const beneficiary = {1451        V2: {1452          parents: 0,1453          interior: {1454            X1: {1455              AccountId32: {1456                network: 'Any',1457                id: randomAccount.addressRaw,1458              },1459            },1460          },1461        },1462      };14631464      const assets = {1465        V2: [1466          {1467            id: {1468              Concrete: {1469                parents: 1,1470                interior: {1471                  X1: {1472                    Parachain: UNIQUE_CHAIN,1473                  },1474                },1475              },1476            },1477            fun: {1478              Fungible: unqFromAstarTransfered,1479            },1480          },1481        ],1482      };14831484      // Initial balance is 1 ASTR1485      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1486      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1487      expect(balanceASTRbefore).to.eq(astarInitialBalance);14881489      const feeAssetItem = 0;1490      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1491      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);14921493      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1494      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1495      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);14961497      // Assert: xcUNQ balance correctly decreased1498      expect(xcUNQbalance).to.eq(unqOnAstarLeft);1499      // Assert: ASTR balance is 0.996...1500      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1501    });15021503    await helper.wait.newBlocks(3);1504    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1505    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1506    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1507  });15081509  itSub('Astar can send only up to its balance', async ({helper}) => {1510    // set Astar's sovereign account's balance1511    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1512    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1513    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);15141515    const moreThanAstarHas = astarBalance * 2n;15161517    let targetAccountBalance = 0n;1518    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15191520    const uniqueMultilocation = {1521      V2: {1522        parents: 1,1523        interior: {1524          X1: {Parachain: UNIQUE_CHAIN},1525        },1526      },1527    };15281529    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1530      targetAccount.addressRaw,1531      {1532        Concrete: {1533          parents: 0,1534          interior: 'Here',1535        },1536      },1537      moreThanAstarHas,1538    );15391540    let maliciousXcmProgramSent: any;1541    const maxWaitBlocks = 3;15421543    // Try to trick Unique1544    await usingAstarPlaygrounds(astarUrl, async (helper) => {1545      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);15461547      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1548    });15491550    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1551        && event.outcome.isFailedToTransactAsset);15521553    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1554    expect(targetAccountBalance).to.be.equal(0n);15551556    // But Astar still can send the correct amount1557    const validTransferAmount = astarBalance / 2n;1558    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1559      targetAccount.addressRaw,1560      {1561        Concrete: {1562          parents: 0,1563          interior: 'Here',1564        },1565      },1566      validTransferAmount,1567    );15681569    await usingAstarPlaygrounds(astarUrl, async (helper) => {1570      await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1571    });15721573    await helper.wait.newBlocks(maxWaitBlocks);15741575    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1576    expect(targetAccountBalance).to.be.equal(validTransferAmount);1577  });15781579  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1580    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1581    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15821583    const uniqueMultilocation = {1584      V2: {1585        parents: 1,1586        interior: {1587          X1: {1588            Parachain: UNIQUE_CHAIN,1589          },1590        },1591      },1592    };15931594    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1595      targetAccount.addressRaw,1596      {1597        Concrete: {1598          parents: 1,1599          interior: {1600            X1: {1601              Parachain: UNIQUE_CHAIN,1602            },1603          },1604        },1605      },1606      testAmount,1607    );16081609    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1610      targetAccount.addressRaw,1611      {1612        Concrete: {1613          parents: 0,1614          interior: 'Here',1615        },1616      },1617      testAmount,1618    );16191620    let maliciousXcmProgramFullIdSent: any;1621    let maliciousXcmProgramHereIdSent: any;1622    const maxWaitBlocks = 3;16231624    // Try to trick Unique using full UNQ identification1625    await usingAstarPlaygrounds(astarUrl, async (helper) => {1626      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);16271628      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1629    });16301631    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1632        && event.outcome.isUntrustedReserveLocation);16331634    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1635    expect(accountBalance).to.be.equal(0n);16361637    // Try to trick Unique using shortened UNQ identification1638    await usingAstarPlaygrounds(astarUrl, async (helper) => {1639      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);16401641      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1642    });16431644    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1645        && event.outcome.isUntrustedReserveLocation);16461647    accountBalance = await helper.balance.getSubstrate(targetAccount.address);1648    expect(accountBalance).to.be.equal(0n);1649  });1650});