difftreelog
feat(test xcm) added generic `low level` xcm tests
in: master
4 files changed
tests/package.jsondiffbeforeafterboth1{2 "name": "unique-tests",3 "version": "1.0.0",4 "description": "Unique Chain Tests",5 "main": "",6 "devDependencies": {7 "@polkadot/typegen": "10.9.1",8 "@types/chai": "^4.3.3",9 "@types/chai-as-promised": "^7.1.5",10 "@types/chai-like": "^1.1.1",11 "@types/chai-subset": "^1.3.3",12 "@types/mocha": "^10.0.0",13 "@types/node": "^20.4.2",14 "@typescript-eslint/eslint-plugin": "^6.0.0",15 "@typescript-eslint/parser": "^6.0.0",16 "chai": "^4.3.6",17 "chai-subset": "^1.6.0",18 "eslint": "^8.45.0",19 "eslint-plugin-mocha": "^10.1.0",20 "mocha": "^10.1.0",21 "mochawesome": "^7.1.3",22 "ts-node": "^10.9.1",23 "typescript": "^5.1.6"24 },25 "mocha": {26 "timeout": 9999999,27 "require": [28 "ts-node/register"29 ]30 },31 "scripts": {32 "lint": "eslint --ext .ts,.js src/",33 "fix": "yarn lint --fix",34 "setup": "ts-node --esm ./src/util/globalSetup.ts",35 "setIdentities": "ts-node --esm ./src/util/identitySetter.ts",36 "checkRelayIdentities": "ts-node --esm ./src/util/relayIdentitiesChecker.ts",37 "frankenstein": "ts-node --esm ./src/util/frankenstein.ts",38 "_test": "yarn setup && mocha --timeout 9999999 --loader=ts-node/esm.mjs",39 "_testParallel": "yarn setup && mocha --timeout 9999999 --parallel --loader=ts-node/esm.mjs",40 "test": "yarn _test './src/**/*.*test.ts'",41 "testParallelFull": "yarn testParallel && yarn testSequential",42 "testParallel": "yarn _testParallel './src/**/*.test.ts'",43 "testSequential": "yarn _test './src/**/*.seqtest.ts'",44 "testStructure": "yarn _test ./**/nesting/*.*test.ts",45 "testGovernance": "RUN_GOV_TESTS=1 yarn _test ./**/governance/*.*test.ts",46 "testEth": "yarn _test './**/eth/**/*.*test.ts'",47 "testEthNesting": "yarn _test './**/eth/nesting/**/*.*test.ts'",48 "testEthFractionalizer": "yarn _test './**/eth/fractionalizer/**/*.*test.ts'",49 "testEthMarketplace": "yarn _test './**/eth/marketplace/**/*.*test.ts'",50 "testEthMarket": "yarn _test './**/eth/marketplace-v2/**/*.*test.ts'",51 "testSub": "yarn _test './**/sub/**/*.*test.ts'",52 "testSubNesting": "yarn _test './**/sub/nesting/**/*.*test.ts'",53 "testEvent": "yarn _test ./src/check-event/*.*test.ts",54 "testEthPayable": "yarn _test './**/eth/payable.test.ts'",55 "testEvmCoder": "yarn _test './**/eth/evmCoder.test.ts'",56 "testNesting": "yarn _test ./**/nest.test.ts",57 "testUnnesting": "yarn _test ./**/unnest.test.ts",58 "testProperties": "yarn _test ./**/collectionProperties.*test.ts ./**/tokenProperties.*test.ts ./**/getPropertiesRpc.test.ts",59 "testCollectionProperties": "yarn _test ./**/collectionProperties.*test.ts",60 "testTokenProperties": "yarn _test ./**/tokenProperties.*test.ts",61 "testMigration": "yarn _test ./**/nesting/migration-check.test.ts",62 "testAddCollectionAdmin": "yarn _test ./**/addCollectionAdmin.test.ts",63 "testSetCollectionLimits": "yarn _test ./**/setCollectionLimits.test.ts",64 "testChangeCollectionOwner": "yarn _test ./**/change-collection-owner.test.ts",65 "testSetCollectionSponsor": "yarn _test ./**/setCollectionSponsor.test.ts",66 "testConfirmSponsorship": "yarn _test ./**/confirmSponsorship.test.ts",67 "testRemoveCollectionAdmin": "yarn _test ./**/removeCollectionAdmin.test.ts",68 "testRemoveCollectionSponsor": "yarn _test ./**/removeCollectionSponsor.test.ts",69 "testAllowLists": "yarn _test ./**/allowLists.test.ts",70 "testConnection": "yarn _test ./**/connection.test.ts",71 "testContracts": "yarn _test ./**/contracts.test.ts",72 "testCreateItem": "yarn _test ./**/createItem.test.ts",73 "testCreateMultipleItems": "yarn _test ./**/createMultipleItems.test.ts",74 "testCreateMultipleItemsEx": "yarn _test ./**/createMultipleItemsEx.test.ts",75 "testApprove": "yarn _test ./**/approve.test.ts",76 "testTransferFrom": "yarn _test ./**/transferFrom.test.ts",77 "testCreateCollection": "yarn _test ./**/createCollection.test.ts",78 "testDestroyCollection": "yarn _test ./**/destroyCollection.test.ts",79 "testToggleContractAllowList": "yarn _test ./**/toggleContractAllowList.test.ts",80 "testAddToContractAllowList": "yarn _test ./**/addToContractAllowList.test.ts",81 "testTransfer": "yarn _test ./**/transfer.test.ts",82 "testBurnItem": "yarn _test ./**/burnItem.test.ts",83 "testAdminTransferAndBurn": "yarn _test ./**/adminTransferAndBurn.test.ts",84 "testSetPermissions": "yarn _test ./**/setPermissions.test.ts",85 "testCreditFeesToTreasury": "yarn _test ./**/creditFeesToTreasury.seqtest.ts",86 "testContractSponsoring": "yarn _test ./**/eth/contractSponsoring.test.ts",87 "testEnableContractSponsoring": "yarn _test ./**/enableContractSponsoring.test.ts",88 "testRemoveFromContractAllowList": "yarn _test ./**/removeFromContractAllowList.test.ts",89 "testSetContractSponsoringRateLimit": "yarn _test ./**/setContractSponsoringRateLimit.test.ts",90 "testSetOffchainSchema": "yarn _test ./**/setOffchainSchema.test.ts",91 "testNextSponsoring": "yarn _test ./**/nextSponsoring.test.ts",92 "testOverflow": "yarn _test ./**/overflow.test.ts",93 "testMaintenance": "yarn _test ./**/maintenance.seqtest.ts",94 "testInflation": "yarn _test ./**/inflation.seqtest.ts",95 "testScheduler": "yarn _test ./**/scheduler.seqtest.ts",96 "testSchedulingEVM": "yarn _test ./**/eth/scheduling.test.ts",97 "testPalletPresence": "yarn _test ./**/pallet-presence.test.ts",98 "testEnableDisableTransfers": "yarn _test ./**/enableDisableTransfer.test.ts",99 "testLimits": "yarn _test ./**/limits.test.ts",100 "testEthCreateNFTCollection": "yarn _test ./**/eth/createNFTCollection.test.ts",101 "testEthCreateRFTCollection": "yarn _test ./**/eth/createRFTCollection.test.ts",102 "testEthNFT": "yarn _test ./**/eth/nonFungible.test.ts",103 "testRFT": "yarn _test ./**/refungible.test.ts",104 "testEthRFT": "yarn _test ./**/eth/reFungible.test.ts ./**/eth/reFungibleToken.test.ts",105 "testFT": "yarn _test ./**/fungible.test.ts",106 "testEthFT": "yarn _test ./**/eth/fungible.test.ts",107 "testRPC": "yarn _test ./**/rpc.test.ts",108 "testPromotion": "yarn _test ./**/appPromotion/*test.ts",109 "testApiConsts": "yarn _test ./**/apiConsts.test.ts",110 "testCouncil": "yarn _test ./**/council.*test.ts",111 "testDemocracy": "yarn _test ./**/democracy.*test.ts",112 "testCollators": "RUN_COLLATOR_TESTS=1 yarn _test ./**/collator-selection/**.*test.ts --timeout 49999999",113 "testCollatorSelection": "RUN_COLLATOR_TESTS=1 yarn _test ./**/collatorSelection.*test.ts --timeout 49999999",114 "testIdentity": "RUN_COLLATOR_TESTS=1 yarn _test ./**/identity.*test.ts --timeout 49999999",115 "testLowLevelXcmUnique": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/lowLevelXcmUnique.test.ts",116 "testXcmUnique": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/xcmUnique.test.ts",117 "testXcmQuartz": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/xcmQuartz.test.ts",118 "testXcmOpal": "RUN_XCM_TESTS=1 yarn _test ./**/xcm/xcmOpal.test.ts",119 "testXcmTransferAcala": "yarn _test ./**/xcm/xcmTransferAcala.test.ts acalaId=2000 uniqueId=5000",120 "testXcmTransferStatemine": "yarn _test ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000",121 "testXcmTransferMoonbeam": "yarn _test ./**/xcm/xcmTransferMoonbeam.test.ts",122 "benchMintingFee": "ts-node src/benchmarks/mintFee/benchmark.ts",123 "load": "yarn _test './**/*.load.ts'",124 "loadTransfer": "ts-node src/transfer.nload.ts",125 "polkadot-types-fetch-metadata": "yarn ts-node --esm src/fetchMetadata.ts",126 "polkadot-types-from-defs": "ts-node --esm ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .",127 "polkadot-types-from-chain": "ts-node --esm ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",128 "polkadot-types": "echo \"export default {}\" > src/interfaces/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain",129 "generateEnv": "ts-node --esm ./src/generateEnv.ts"130 },131 "author": "",132 "license": "SEE LICENSE IN ../LICENSE",133 "homepage": "",134 "dependencies": {135 "@openzeppelin/contracts": "^4.9.2",136 "@polkadot/api": "10.9.1",137 "@polkadot/rpc-core": "^10.9.1",138 "@polkadot/util": "12.3.2",139 "@polkadot/util-crypto": "12.3.2",140 "@polkadot/wasm-crypto-asmjs": "^7.2.1",141 "@polkadot/wasm-crypto-wasm": "^7.2.1",142 "@rmrk-team/evm-contracts": "^1.2.1",143 "@typechain/web3-v1": "^6.0.3",144 "chai-as-promised": "^7.1.1",145 "chai-like": "^1.1.1",146 "csv-writer": "^1.6.0",147 "find-process": "^1.4.7",148 "lossless-json": "^2.0.9",149 "solc": "0.8.20",150 "typechain": "^8.2.0",151 "web3": "1.10.0"152 },153 "resolutions": {154 "decode-uri-component": "^0.2.1"155 },156 "type": "module",157 "packageManager": "yarn@3.6.1"158}tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -458,9 +458,9 @@
session: SessionGroup;
testUtils: TestUtilGroup;
foreignAssets: ForeignAssetsGroup;
- xcm: XcmGroup<UniqueHelper>;
- xTokens: XTokensGroup<UniqueHelper>;
- tokens: TokensGroup<UniqueHelper>;
+ xcm: XcmGroup<DevUniqueHelper>;
+ xTokens: XTokensGroup<DevUniqueHelper>;
+ tokens: TokensGroup<DevUniqueHelper>;
scheduler: SchedulerGroup;
collatorSelection: CollatorSelectionGroup;
council: ICollectiveGroup;
@@ -627,10 +627,10 @@
super(logger, options);
this.wait = new WaitGroup(this);
}
- getSudo<T extends AcalaHelper>() {
+ getSudo() {
// eslint-disable-next-line @typescript-eslint/naming-convention
const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
+ return this.clone(SudoHelperType) as DevAcalaHelper;
}
}
@@ -643,10 +643,10 @@
this.wait = new WaitGroup(this);
}
- getSudo<T extends PolkadexHelper>() {
+ getSudo() {
// eslint-disable-next-line @typescript-eslint/naming-convention
const SudoHelperType = SudoHelper(this.helperBase);
- return this.clone(SudoHelperType) as T;
+ return this.clone(SudoHelperType) as DevPolkadexHelper;
}
}
@@ -1290,7 +1290,8 @@
} else if(maxBlocksToWait > 0) {
maxBlocksToWait--;
} else {
- this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found`);
+ this.helper.logger.log(`Eligible event \`${eventIdStr}\` is NOT found.
+ The wait lasted until block ${blockNumber} inclusive`);
unsubscribe();
resolve(null);
}
tests/src/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/xcm/lowLevelXcmUnique.test.ts
@@ -0,0 +1,775 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {IKeyringPair} from '@polkadot/types/types';
+import config from '../config';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';
+import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
+import {nToBigInt} from '@polkadot/util';
+import {hexToString} from '@polkadot/util';
+
+const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
+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 acalaUrl = config.acalaUrl;
+const moonbeamUrl = config.moonbeamUrl;
+const astarUrl = config.astarUrl;
+const polkadexUrl = config.polkadexUrl;
+
+const ASTAR_DECIMALS = 18n;
+const UNQ_DECIMALS = 18n;
+
+const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;
+const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
+const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
+const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;
+const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;
+
+const SAFE_XCM_VERSION = 2;
+const maxWaitBlocks = 6;
+
+const uniqueMultilocation = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ },
+};
+
+let balanceUniqueTokenInit: bigint;
+let balanceUniqueTokenMiddle: bigint;
+let balanceUniqueTokenFinal: bigint;
+let unqFees: bigint;
+
+const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
+ await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
+ && event.outcome.isFailedToTransactAsset);
+};
+const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {
+ await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
+ && event.outcome.isUntrustedReserveLocation);
+};
+
+const NETWORKS = {
+ acala: usingAcalaPlaygrounds,
+ astar: usingAstarPlaygrounds,
+ polkadex: usingPolkadexPlaygrounds,
+ moonbeam: usingMoonbeamPlaygrounds,
+} as const;
+
+function mapToChainId(networkName: keyof typeof NETWORKS) {
+ switch (networkName) {
+ case 'acala':
+ return ACALA_CHAIN;
+ case 'astar':
+ return ASTAR_CHAIN;
+ case 'moonbeam':
+ return MOONBEAM_CHAIN;
+ case 'polkadex':
+ return POLKADEX_CHAIN;
+ }
+}
+
+function mapToChainUrl(networkName: keyof typeof NETWORKS): string {
+ switch (networkName) {
+ case 'acala':
+ return acalaUrl;
+ case 'astar':
+ return astarUrl;
+ case 'moonbeam':
+ return moonbeamUrl;
+ case 'polkadex':
+ return polkadexUrl;
+ }
+}
+
+function getDevPlayground<T extends keyof typeof NETWORKS>(name: T) {
+ return NETWORKS[name];
+}
+
+
+async function genericSendUnqTo(
+ networkName: keyof typeof NETWORKS,
+ randomAccount: IKeyringPair,
+ randomAccountOnTargetChain = randomAccount,
+) {
+ const networkUrl = mapToChainUrl(networkName);
+ const targetPlayground = getDevPlayground(networkName);
+ await usingPlaygrounds(async (helper) => {
+ balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+ const destination = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: mapToChainId(networkName),
+ },
+ },
+ },
+ };
+
+ const beneficiary = {
+ V2: {
+ parents: 0,
+ interior: {
+ X1: (
+ networkName == 'moonbeam' ?
+ {
+ AccountKey20: {
+ network: 'Any',
+ key: randomAccountOnTargetChain.address,
+ },
+ }
+ :
+ {
+ AccountId32: {
+ network: 'Any',
+ id: randomAccountOnTargetChain.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 -> %s] transaction fees on Unique: %s UNQ', networkName, helper.util.bigIntToDecimals(unqFees));
+ expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
+
+ await targetPlayground(networkUrl, async (helper) => {
+ /*
+ Since only the parachain part of the Polkadex
+ infrastructure is launched (without their
+ solochain validators), processing incoming
+ assets will lead to an error.
+ This error indicates that the Polkadex chain
+ received a message from the Unique network,
+ since the hash is being checked to ensure
+ it matches what was sent.
+ */
+ if(networkName == 'polkadex') {
+ await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);
+ } else {
+ await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash);
+ }
+ });
+
+ });
+}
+
+async function genericSendUnqBack(
+ networkName: keyof typeof NETWORKS,
+ sudoer: IKeyringPair,
+ randomAccountOnUnq: IKeyringPair,
+) {
+ const networkUrl = mapToChainUrl(networkName);
+
+ const targetPlayground = getDevPlayground(networkName);
+ await usingPlaygrounds(async (helper) => {
+
+ const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+ randomAccountOnUnq.addressRaw,
+ {
+ Concrete: {
+ parents: 1,
+ interior: {
+ X1: {Parachain: UNIQUE_CHAIN},
+ },
+ },
+ },
+ SENDBACK_AMOUNT,
+ );
+
+ let xcmProgramSent: any;
+
+
+ await targetPlayground(networkUrl, async (helper) => {
+ if('getSudo' in helper) {
+ await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, xcmProgram);
+ xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ } else if('fastDemocracy' in helper) {
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, xcmProgram]);
+ // Needed to bypass the call filter.
+ const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+ await helper.fastDemocracy.executeProposal('sending MoonBeam -> Unique via XCM program', batchCall);
+ 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(randomAccountOnUnq.address);
+
+ expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees - STAYED_ON_TARGET_CHAIN);
+
+ });
+}
+
+async function genericSendOnlyOwnedBalance(
+ networkName: keyof typeof NETWORKS,
+ sudoer: IKeyringPair,
+) {
+ const networkUrl = mapToChainUrl(networkName);
+ const targetPlayground = getDevPlayground(networkName);
+
+ const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);
+
+ await usingPlaygrounds(async (helper) => {
+ const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName));
+ await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance);
+ const moreThanTargetChainHas = 2n * targetChainBalance;
+
+ const targetAccount = helper.arrange.createEmptyAccount();
+
+ const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ moreThanTargetChainHas,
+ );
+
+ let maliciousXcmProgramSent: any;
+
+
+ await targetPlayground(networkUrl, async (helper) => {
+ if('getSudo' in helper) {
+ await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, maliciousXcmProgram);
+ maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ } else if('fastDemocracy' in helper) {
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);
+ // Needed to bypass the call filter.
+ const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+ await helper.fastDemocracy.executeProposal('sending MoonBeam -> Unique via XCM program', batchCall);
+ maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ }
+ });
+
+ await expectFailedToTransact(helper, maliciousXcmProgramSent);
+
+ const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(targetAccountBalance).to.be.equal(0n);
+ });
+}
+
+async function genericReserveTransferUNQfrom(netwokrName: keyof typeof NETWORKS, sudoer: IKeyringPair) {
+ const networkUrl = mapToChainUrl(netwokrName);
+ const targetPlayground = getDevPlayground(netwokrName);
+
+ await usingPlaygrounds(async (helper) => {
+ const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+ const targetAccount = helper.arrange.createEmptyAccount();
+
+ const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ },
+ },
+ testAmount,
+ );
+
+ const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+ targetAccount.addressRaw,
+ {
+ Concrete: {
+ parents: 0,
+ interior: 'Here',
+ },
+ },
+ testAmount,
+ );
+
+ let maliciousXcmProgramFullIdSent: any;
+ let maliciousXcmProgramHereIdSent: any;
+ const maxWaitBlocks = 3;
+
+ // Try to trick Unique using full UNQ identification
+ await targetPlayground(networkUrl, async (helper) => {
+ if('getSudo' in helper) {
+ await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, maliciousXcmProgramFullId);
+ maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ }
+ // Moonbeam case
+ else if('fastDemocracy' in helper) {
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);
+
+ // Needed to bypass the call filter.
+ const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+ await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);
+
+ maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ }
+ });
+
+
+ await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);
+
+ let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(accountBalance).to.be.equal(0n);
+
+ // Try to trick Unique using shortened UNQ identification
+ await targetPlayground(networkUrl, async (helper) => {
+ if('getSudo' in helper) {
+ await helper.getSudo().xcm.send(sudoer, uniqueMultilocation, maliciousXcmProgramHereId);
+ maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ }
+ else if('fastDemocracy' in helper) {
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);
+
+ // Needed to bypass the call filter.
+ const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+ await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);
+
+ maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ }
+ });
+
+ await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);
+
+ accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+ expect(accountBalance).to.be.equal(0n);
+ });
+}
+
+async function genericRejectNativeToknsFrom(netwokrName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {
+ const networkUrl = mapToChainUrl(netwokrName);
+ const targetPlayground = getDevPlayground(netwokrName);
+ let messageSent: any;
+
+ await usingPlaygrounds(async (helper) => {
+ const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+ helper.arrange.createEmptyAccount().addressRaw,
+ {
+ Concrete: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: mapToChainId(netwokrName),
+ },
+ },
+ },
+ },
+ TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT,
+ );
+ await targetPlayground(networkUrl, async (helper) => {
+ if('getSudo' in helper) {
+ await helper.getSudo().xcm.send(sudoerOnTargetChain, uniqueMultilocation, maliciousXcmProgramFullId);
+ messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ } else if('fastDemocracy' in helper) {
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);
+
+ // Needed to bypass the call filter.
+ const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+ await helper.fastDemocracy.executeProposal('sending native tokens to the Unique via fast democracy', batchCall);
+
+ messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+ }
+ });
+
+ await expectFailedToTransact(helper, messageSent);
+ });
+}
+
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ console.log(config.acalaUrl);
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+ const destination = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ },
+ };
+
+ const metadata = {
+ name: 'Unique Network',
+ symbol: 'UNQ',
+ decimals: 18,
+ minimalBalance: 1250_000_000_000_000_000n,
+ };
+ const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
+ hexToString(v.toJSON()['symbol'])) as string[];
+
+ if(!assets.includes('UNQ')) {
+ await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ } else {
+ console.log('UNQ token already registered on Acala assetRegistry pallet');
+ }
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+ });
+
+ await usingPlaygrounds(async (helper) => {
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+ balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+ });
+ });
+
+ itSub('Should connect and send UNQ to Acala', async () => {
+ await genericSendUnqTo('acala', randomAccount);
+ });
+
+ itSub('Should connect to Acala and send UNQ back', async () => {
+ await genericSendUnqBack('acala', alice, randomAccount);
+ });
+
+ itSub('Acala can send only up to its balance', async () => {
+ await genericSendOnlyOwnedBalance('acala', alice);
+ });
+
+ itSub('Should not accept reserve transfer of UNQ from Acala', async () => {
+ await genericReserveTransferUNQfrom('acala', alice);
+ });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+
+ const uniqueAssetId = {
+ Concrete: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ },
+ };
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ randomAccount = helper.arrange.createEmptyAccount();
+
+ // 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;
+ /*
+ Check whether the Unique token has been added
+ to the whitelist, since an error will occur
+ if it is added again. Needed for debugging
+ when this test is run multiple times.
+ */
+ 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, SENDER_BUDGET);
+ balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+ });
+ });
+
+ itSub('Should connect and send UNQ to Polkadex', async () => {
+ await genericSendUnqTo('polkadex', randomAccount);
+ });
+
+
+ itSub('Should connect to Polkadex and send UNQ back', async () => {
+ await genericSendUnqBack('polkadex', alice, randomAccount);
+ });
+
+ itSub('Polkadex can send only up to its balance', async () => {
+ await genericSendOnlyOwnedBalance('polkadex', alice);
+ });
+
+ itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {
+ await genericReserveTransferUNQfrom('polkadex', alice);
+ });
+});
+
+// These tests are relevant only when
+// the the corresponding foreign assets are not registered
+describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => {
+ let alice: IKeyringPair;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+ });
+
+ itSub('Unique rejects ACA tokens from Acala', async () => {
+ await genericRejectNativeToknsFrom('acala', alice);
+ });
+
+ itSub('Unique rejects GLMR tokens from Moonbeam', async () => {
+ await genericRejectNativeToknsFrom('moonbeam', alice);
+ });
+
+ itSub('Unique rejects ASTR tokens from Astar', async () => {
+ await genericRejectNativeToknsFrom('astar', alice);
+ });
+
+ itSub('Unique rejects PDX tokens from Polkadex', async () => {
+ await genericRejectNativeToknsFrom('polkadex', alice);
+ });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => {
+ // Unique constants
+ let alice: IKeyringPair;
+ let uniqueAssetLocation;
+
+ let randomAccountUnique: IKeyringPair;
+ let randomAccountMoonbeam: IKeyringPair;
+
+ // Moonbeam constants
+ let assetId: string;
+
+ const uniqueAssetMetadata = {
+ name: 'xcUnique',
+ symbol: 'xcUNQ',
+ decimals: 18,
+ isFrozen: false,
+ minimalBalance: 1n,
+ };
+
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ [randomAccountUnique] = 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 usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+ const alithAccount = helper.account.alithAccount();
+ const baltatharAccount = helper.account.baltatharAccount();
+ const dorothyAccount = helper.account.dorothyAccount();
+
+ randomAccountMoonbeam = helper.account.create();
+
+ // >>> Sponsoring Dorothy >>>
+ console.log('Sponsoring Dorothy.......');
+ await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
+ console.log('Sponsoring Dorothy.......DONE');
+ // <<< Sponsoring Dorothy <<<
+ uniqueAssetLocation = {
+ XCM: {
+ parents: 1,
+ interior: {X1: {Parachain: UNIQUE_CHAIN}},
+ },
+ };
+ const existentialDeposit = 1n;
+ const isSufficient = true;
+ const unitsPerSecond = 1n;
+ const numAssetsWeightHint = 0;
+
+ if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {
+ console.log('Unique asset already registered');
+ } else {
+ const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+ location: uniqueAssetLocation,
+ metadata: uniqueAssetMetadata,
+ existentialDeposit,
+ isSufficient,
+ unitsPerSecond,
+ numAssetsWeightHint,
+ });
+
+ console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+
+ await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
+
+ // >>> Acquire Unique AssetId Info on Moonbeam >>>
+ console.log('Acquire Unique AssetId Info on Moonbeam.......');
+
+ assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
+
+ console.log('UNQ asset ID is %s', assetId);
+ console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
+ }
+ // >>> Acquire Unique AssetId Info on Moonbeam >>>
+
+ // >>> Sponsoring random Account >>>
+ console.log('Sponsoring random Account.......');
+ await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);
+ console.log('Sponsoring random Account.......DONE');
+ // <<< Sponsoring random Account <<<
+ });
+
+ await usingPlaygrounds(async (helper) => {
+ await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);
+ balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);
+ });
+ });
+
+ itSub('Should connect and send UNQ to Moonbeam', async () => {
+ await genericSendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);
+ });
+
+ itSub('Should connect to Moonbeam and send UNQ back', async () => {
+ await genericSendUnqBack('moonbeam', alice, randomAccountUnique);
+ });
+
+ itSub('Moonbeam can send only up to its balance', async () => {
+ await genericSendOnlyOwnedBalance('moonbeam', alice);
+ });
+
+ itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {
+ await genericReserveTransferUNQfrom('moonbeam', alice);
+ });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => {
+ let alice: IKeyringPair;
+ let randomAccount: IKeyringPair;
+
+ const UNQ_ASSET_ID_ON_ASTAR = 1;
+ const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;
+
+ // Unique -> Astar
+ const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
+ const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ alice = await privateKey('//Alice');
+ randomAccount = helper.arrange.createEmptyAccount();
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+ console.log('randomAccount', randomAccount.address);
+
+ // Set the default version to wrap the first message to other chains.
+ await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+ });
+
+ await usingAstarPlaygrounds(astarUrl, async (helper) => {
+ if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {
+ console.log('1. Create foreign asset and metadata');
+ // TODO update metadata with values from production
+ await helper.assets.create(
+ alice,
+ UNQ_ASSET_ID_ON_ASTAR,
+ alice.address,
+ UNQ_MINIMAL_BALANCE_ON_ASTAR,
+ );
+
+ await helper.assets.setMetadata(
+ alice,
+ UNQ_ASSET_ID_ON_ASTAR,
+ 'Cross chain UNQ',
+ 'xcUNQ',
+ Number(UNQ_DECIMALS),
+ );
+
+ console.log('2. Register asset location on Astar');
+ const assetLocation = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ },
+ };
+
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
+
+ console.log('3. Set UNQ payment for XCM execution on Astar');
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ }
+ console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);
+ });
+ });
+
+ itSub('Should connect and send UNQ to Astar', async () => {
+ await genericSendUnqTo('astar', randomAccount);
+ });
+
+ itSub('Should connect to Astar and send UNQ back', async () => {
+ await genericSendUnqBack('astar', alice, randomAccount);
+ });
+
+ itSub('Astar can send only up to its balance', async () => {
+ await genericSendOnlyOwnedBalance('astar', alice);
+ });
+
+ itSub('Should not accept reserve transfer of UNQ from Astar', async () => {
+ await genericReserveTransferUNQfrom('astar', alice);
+ });
+});
tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -18,7 +18,7 @@
import config from '../config';
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';
+import {hexToString, nToBigInt} from '@polkadot/util';
const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);
@@ -57,6 +57,18 @@
const SAFE_XCM_VERSION = 2;
const maxWaitBlocks = 6;
+
+const uniqueMultilocation = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
+ },
+ },
+};
+
const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
&& event.outcome.isFailedToTransactAsset);
@@ -509,11 +521,15 @@
decimals: 18,
minimalBalance: 1250000000000000000n,
};
+ const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
+ hexToString(v.toJSON()['symbol'])) as string[];
- await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ if(!assets.includes('UNQ')) {
+ await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ } else {
+ console.log('UNQ token already registered on Acala assetRegistry pallet');
+ }
await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
- balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);
- balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
});
await usingPlaygrounds(async (helper) => {
@@ -669,15 +685,6 @@
let targetAccountBalance = 0n;
const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
- const uniqueMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {Parachain: UNIQUE_CHAIN},
- },
- },
- };
const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
targetAccount.addressRaw,
@@ -925,15 +932,6 @@
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,
{
@@ -971,15 +969,6 @@
const targetAccount = helper.arrange.createEmptyAccount();
- const uniqueMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {Parachain: UNIQUE_CHAIN},
- },
- },
- };
-
const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
targetAccount.addressRaw,
{
@@ -1304,26 +1293,30 @@
const unitsPerSecond = 1n;
const numAssetsWeightHint = 0;
- const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
- location: uniqueAssetLocation,
- metadata: uniqueAssetMetadata,
- existentialDeposit,
- isSufficient,
- unitsPerSecond,
- numAssetsWeightHint,
- });
+ if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON) {
+ console.log('Unique asset is already registered on MoonBeam');
+ } else {
+ const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+ location: uniqueAssetLocation,
+ metadata: uniqueAssetMetadata,
+ existentialDeposit,
+ isSufficient,
+ unitsPerSecond,
+ numAssetsWeightHint,
+ });
- console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+ console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
- await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
+ await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
- // >>> Acquire Unique AssetId Info on Moonbeam >>>
- console.log('Acquire Unique AssetId Info on Moonbeam.......');
+ // >>> Acquire Unique AssetId Info on Moonbeam >>>
+ console.log('Acquire Unique AssetId Info on Moonbeam.......');
- assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
+ assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
- console.log('UNQ asset ID is %s', assetId);
- console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
+ console.log('UNQ asset ID is %s', assetId);
+ console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
+ }
// >>> Acquire Unique AssetId Info on Moonbeam >>>
// >>> Sponsoring random Account >>>
@@ -1455,15 +1448,6 @@
let targetAccountBalance = 0n;
const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
- const uniqueMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {Parachain: UNIQUE_CHAIN},
- },
- },
- };
const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
targetAccount.addressRaw,
@@ -1527,17 +1511,6 @@
const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
- const uniqueMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {
- Parachain: UNIQUE_CHAIN,
- },
- },
- },
- };
-
const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
targetAccount.addressRaw,
{
@@ -1634,40 +1607,41 @@
});
await usingAstarPlaygrounds(astarUrl, async (helper) => {
- console.log('1. Create foreign asset and metadata');
- // TODO update metadata with values from production
- await helper.assets.create(
- alice,
- UNQ_ASSET_ID_ON_ASTAR,
- alice.address,
- UNQ_MINIMAL_BALANCE_ON_ASTAR,
- );
+ if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {
+ console.log('1. Create foreign asset and metadata');
+ // TODO update metadata with values from production
+ await helper.assets.create(
+ alice,
+ UNQ_ASSET_ID_ON_ASTAR,
+ alice.address,
+ UNQ_MINIMAL_BALANCE_ON_ASTAR,
+ );
- await helper.assets.setMetadata(
- alice,
- UNQ_ASSET_ID_ON_ASTAR,
- 'Cross chain UNQ',
- 'xcUNQ',
- Number(UNQ_DECIMALS),
- );
+ await helper.assets.setMetadata(
+ alice,
+ UNQ_ASSET_ID_ON_ASTAR,
+ 'Cross chain UNQ',
+ 'xcUNQ',
+ Number(UNQ_DECIMALS),
+ );
- console.log('2. Register asset location on Astar');
- const assetLocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {
- Parachain: UNIQUE_CHAIN,
+ console.log('2. Register asset location on Astar');
+ const assetLocation = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: UNIQUE_CHAIN,
+ },
},
},
- },
- };
-
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
+ };
- console.log('3. Set UNQ payment for XCM execution on Astar');
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
+ console.log('3. Set UNQ payment for XCM execution on Astar');
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ }
console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');
await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);
});
@@ -1824,15 +1798,6 @@
let targetAccountBalance = 0n;
const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
- const uniqueMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {Parachain: UNIQUE_CHAIN},
- },
- },
- };
const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
targetAccount.addressRaw,
@@ -1887,17 +1852,6 @@
itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {
const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
- const uniqueMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {
- Parachain: UNIQUE_CHAIN,
- },
- },
- },
- };
const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
targetAccount.addressRaw,