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

no changes

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
--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -16,14 +16,16 @@
 
 import {IKeyringPair} from '@polkadot/types/types';
 import config from '../config';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds} from '../util';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';
 import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
+import {nToBigInt} from '@polkadot/util';
 
 const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
 const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);
 const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);
 const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);
 const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);
+const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);
 
 const STATEMINT_PALLET_INSTANCE = 50;
 
@@ -32,6 +34,7 @@
 const acalaUrl = config.acalaUrl;
 const moonbeamUrl = config.moonbeamUrl;
 const astarUrl = config.astarUrl;
+const polkadexUrl = config.polkadexUrl;
 
 const RELAY_DECIMALS = 12;
 const STATEMINT_DECIMALS = 12;
@@ -791,6 +794,193 @@
   });
 });
 
+describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+  let unqFees: bigint;
+  let balanceUniqueTokenInit: bigint;
+  let balanceUniqueTokenMiddle: bigint;
+  let balanceUniqueTokenFinal: bigint;
+  const maxWaitBlocks = 6;
+
+  const uniqueAssetId = {
+    Concrete: {
+      parents: 1,
+      interior: {
+        X1: {
+          Parachain: UNIQUE_CHAIN,
+        },
+      },
+    },
+  };
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', [])).toJSON() as [])
+        .map(nToBigInt).length != 0;
+
+      if(!isWhitelisted) {
+        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);
+      }
+
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
+      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {
+
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: POLKADEX_CHAIN,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V2: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: randomAccount.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V2: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: TRANSFER_AMOUNT,
+          },
+        },
+      ],
+    };
+
+    const feeAssetItem = 0;
+
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+    unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
+    console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
+    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);
+    });
+  });
+
+
+  itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {
+
+    const uniqueMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {Parachain: UNIQUE_CHAIN},
+        },
+      },
+    };
+
+    const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      randomAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      TRANSFER_AMOUNT,
+    );
+
+    let xcmProgramSent: any;
+
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);
+
+      xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);
+
+    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
+
+    expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);
+  });
+
+  itSub('Polkadex can send only up to its balance', async ({helper}) => {
+    const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);
+    const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);
+    const moreThanPolkadexHas = 2n * polkadexBalance;
+
+    const targetAccount = helper.arrange.createEmptyAccount();
+
+    const uniqueMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {Parachain: UNIQUE_CHAIN},
+        },
+      },
+    };
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanPolkadexHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash);
+
+    const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+  });
+});
+
 // These tests are relevant only when
 // the the corresponding foreign assets are not registered
 describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {
@@ -930,6 +1120,31 @@
 
     await expectFailedToTransact(helper, messageSent);
   });
+
+  itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      helper.arrange.createEmptyAccount().addressRaw,
+      {
+        Concrete: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: POLKADEX_CHAIN,
+            },
+          },
+        },
+      },
+      testAmount,
+    );
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
 });
 
 describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {