difftreelog
feat(xcm tests) `XcmHelper` integraion for QTZ\UNQ
in: master
6 files changed
.baedeker/.gitignorediffbeforeafterboth--- a/.baedeker/.gitignore
+++ b/.baedeker/.gitignore
@@ -1,4 +1,5 @@
/.bdk-env
-/rewrites.jsonnet
+/rewrites*.jsonnet
/vendor
/baedeker-library
+!/rewrites.example.jsonnet
\ No newline at end of file
.github/workflows/xcm.ymldiffbeforeafterboth--- a/.github/workflows/xcm.yml
+++ b/.github/workflows/xcm.yml
@@ -38,7 +38,7 @@
with:
matrix: |
network {opal}, relay_branch {${{ env.UNIQUEWEST_MAINNET_BRANCH }}}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.WESTMINT_BUILD_BRANCH }}}, astar_version {${{ env.ASTAR_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testXcmOpal}, runtime_features {opal-runtime}
- network {quartz}, relay_branch {${{ env.KUSAMA_MAINNET_BRANCH }}}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}}, astar_version {${{ env.SHIDEN_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testXcmQuartz}, runtime_features {quartz-runtime}
+ network {quartz}, relay_branch {${{ env.KUSAMA_MAINNET_BRANCH }}}, acala_version {${{ env.KARURA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONRIVER_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINE_BUILD_BRANCH }}}, astar_version {${{ env.SHIDEN_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testFullXcmQuartz}, runtime_features {quartz-runtime}
network {unique}, relay_branch {${{ env.POLKADOT_MAINNET_BRANCH }}}, acala_version {${{ env.ACALA_BUILD_BRANCH }}}, moonbeam_version {${{ env.MOONBEAM_BUILD_BRANCH }}}, cumulus_version {${{ env.STATEMINT_BUILD_BRANCH }}}, astar_version {${{ env.ASTAR_BUILD_BRANCH }}}, polkadex_version {${{ env.POLKADEX_BUILD_BRANCH }}}, runtest {testFullXcmUnique}, runtime_features {unique-runtime}
xcm:
tests/src/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth--- a/tests/src/xcm/lowLevelXcmQuartz.test.ts
+++ b/tests/src/xcm/lowLevelXcmQuartz.test.ts
@@ -15,35 +15,16 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {IKeyringPair} from '@polkadot/types/types';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util';
-import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
-import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT} from './xcm.types';
-
+import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util';
+import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types';
+import {hexToString} from '@polkadot/util';
const testHelper = new XcmTestHelper('quartz');
describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {
let alice: IKeyringPair;
let randomAccount: IKeyringPair;
-
- let balanceQuartzTokenInit: bigint;
- let balanceQuartzTokenMiddle: bigint;
- let balanceQuartzTokenFinal: bigint;
- let balanceKaruraTokenInit: bigint;
- let balanceKaruraTokenMiddle: bigint;
- let balanceKaruraTokenFinal: bigint;
- let balanceQuartzForeignTokenInit: bigint;
- let balanceQuartzForeignTokenMiddle: bigint;
- let balanceQuartzForeignTokenFinal: bigint;
- // computed by a test transfer from prod Quartz to prod Karura.
- // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9
- // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block)
- const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;
- const karuraEps = 8n * 10n ** 16n;
-
- let karuraBackwardTransferAmount: bigint;
-
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
@@ -72,15 +53,19 @@
minimalBalance: 1000000000000000000n,
};
- await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
+ hexToString(v.toJSON()['symbol'])) as string[];
+
+ if(!assets.includes('QTZ')) {
+ await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ } else {
+ console.log('QTZ token already registered on Karura assetRegistry pallet');
+ }
await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
- balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);
- balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
});
await usingPlaygrounds(async (helper) => {
- await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
- balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
});
});
@@ -100,67 +85,17 @@
// the the corresponding foreign assets are not registered
describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {
let alice: IKeyringPair;
- let alith: IKeyringPair;
- const testAmount = 100_000_000_000n;
- let quartzParachainJunction;
- let quartzAccountJunction;
- let quartzParachainMultilocation: any;
- let quartzAccountMultilocation: any;
- let quartzCombinedMultilocation: any;
-
- let messageSent: any;
-
- const maxWaitBlocks = 3;
-
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
- quartzParachainJunction = {Parachain: QUARTZ_CHAIN};
- quartzAccountJunction = {
- AccountId32: {
- network: 'Any',
- id: alice.addressRaw,
- },
- };
-
- quartzParachainMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X1: quartzParachainJunction,
- },
- },
- };
- quartzAccountMultilocation = {
- V2: {
- parents: 0,
- interior: {
- X1: quartzAccountJunction,
- },
- },
- };
- quartzCombinedMultilocation = {
- V2: {
- parents: 1,
- interior: {
- X2: [quartzParachainJunction, quartzAccountJunction],
- },
- },
- };
-
// Set the default version to wrap the first message to other chains.
await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
});
-
- // eslint-disable-next-line require-await
- await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
- alith = helper.account.alithAccount();
- });
});
itSub('Quartz rejects KAR tokens from Karura', async () => {
@@ -195,22 +130,12 @@
minimalBalance: 1n,
};
- let balanceQuartzTokenInit: bigint;
- let balanceQuartzTokenMiddle: bigint;
- let balanceQuartzTokenFinal: bigint;
- let balanceForeignQtzTokenInit: bigint;
- let balanceForeignQtzTokenMiddle: bigint;
- let balanceForeignQtzTokenFinal: bigint;
- let balanceMovrTokenInit: bigint;
- let balanceMovrTokenMiddle: bigint;
- let balanceMovrTokenFinal: bigint;
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
[randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
- balanceForeignQtzTokenInit = 0n;
// Set the default version to wrap the first message to other chains.
await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
@@ -239,20 +164,22 @@
const isSufficient = true;
const unitsPerSecond = 1n;
const numAssetsWeightHint = 0;
+ if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
+ console.log('Quartz asset already registered on Moonriver');
+ } else {
+ const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+ location: quartzAssetLocation,
+ metadata: quartzAssetMetadata,
+ existentialDeposit,
+ isSufficient,
+ unitsPerSecond,
+ numAssetsWeightHint,
+ });
- const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
- location: quartzAssetLocation,
- metadata: quartzAssetMetadata,
- existentialDeposit,
- isSufficient,
- unitsPerSecond,
- numAssetsWeightHint,
- });
-
- console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
-
- await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
+ console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+ await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
+ }
// >>> Acquire Quartz AssetId Info on Moonriver >>>
console.log('Acquire Quartz AssetId Info on Moonriver.......');
@@ -267,13 +194,10 @@
await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);
console.log('Sponsoring random Account.......DONE');
// <<< Sponsoring random Account <<<
-
- balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);
});
await usingPlaygrounds(async (helper) => {
await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
- balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);
});
});
@@ -296,7 +220,7 @@
describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {
let alice: IKeyringPair;
- let sender: IKeyringPair;
+ let randomAccount: IKeyringPair;
const QTZ_ASSET_ID_ON_SHIDEN = 1;
const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n;
@@ -304,71 +228,69 @@
// Quartz -> Shiden
const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
- const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ
- const qtzToShidenArrived = 9_999_999_999_088_000_000n; // 9.999 ... QTZ, Shiden takes a commision in foreign tokens
- // Shiden -> Quartz
- const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ
- const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 4.999_999_999_088_000_000n QTZ
- let balanceAfterQuartzToShidenXCM: bigint;
before(async () => {
await usingPlaygrounds(async (helper, privateKey) => {
alice = await privateKey('//Alice');
- [sender] = await helper.arrange.createAccounts([100n], alice);
- console.log('sender', sender.address);
+ randomAccount = helper.arrange.createEmptyAccount();
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+ console.log('sender: ', randomAccount.address);
// Set the default version to wrap the first message to other chains.
await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
});
await usingShidenPlaygrounds(shidenUrl, async (helper) => {
- console.log('1. Create foreign asset and metadata');
- // TODO update metadata with values from production
- await helper.assets.create(
- alice,
- QTZ_ASSET_ID_ON_SHIDEN,
- alice.address,
- QTZ_MINIMAL_BALANCE_ON_SHIDEN,
- );
+ if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
+ console.log('1. Create foreign asset and metadata');
+ // TODO update metadata with values from production
+ await helper.assets.create(
+ alice,
+ QTZ_ASSET_ID_ON_SHIDEN,
+ alice.address,
+ QTZ_MINIMAL_BALANCE_ON_SHIDEN,
+ );
- await helper.assets.setMetadata(
- alice,
- QTZ_ASSET_ID_ON_SHIDEN,
- 'Cross chain QTZ',
- 'xcQTZ',
- Number(QTZ_DECIMALS),
- );
+ await helper.assets.setMetadata(
+ alice,
+ QTZ_ASSET_ID_ON_SHIDEN,
+ 'Cross chain QTZ',
+ 'xcQTZ',
+ Number(QTZ_DECIMALS),
+ );
- console.log('2. Register asset location on Shiden');
- const assetLocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {
- Parachain: QUARTZ_CHAIN,
+ console.log('2. Register asset location on Shiden');
+ const assetLocation = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
},
},
- },
- };
-
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
+ };
- console.log('3. Set QTZ payment for XCM execution on Shiden');
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
+ console.log('3. Set QTZ payment for XCM execution on Shiden');
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ } else {
+ console.log('QTZ is already registered on Shiden');
+ }
console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
- await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);
+ await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);
});
});
itSub('Should connect and send QTZ to Shiden', async () => {
- await testHelper.sendUnqTo('shiden', sender);
+ await testHelper.sendUnqTo('shiden', randomAccount);
});
itSub('Should connect to Shiden and send QTZ back', async () => {
- await testHelper.sendUnqBack('shiden', alice, sender);
+ await testHelper.sendUnqBack('shiden', alice, randomAccount);
});
itSub('Shiden can send only up to its balance', async () => {
tests/src/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth161617import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, requirePalletsOrSkip, Pallets} from '../util';19import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util';20import {Event} from '../util/playgrounds/unique.dev';21import {nToBigInt} from '@polkadot/util';20import {nToBigInt} from '@polkadot/util';22import {hexToString} from '@polkadot/util';21import {hexToString} from '@polkadot/util';23import {ASTAR_DECIMALS, NETWORKS, SAFE_XCM_VERSION, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, expectFailedToTransact, expectUntrustedReserveLocationFail, getDevPlayground, mapToChainId, mapToChainUrl, maxWaitBlocks, moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types';22import {ASTAR_DECIMALS, SAFE_XCM_VERSION, SENDER_BUDGET, UNIQUE_CHAIN, UNQ_DECIMALS, XcmTestHelper, acalaUrl, astarUrl, moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types';24232526const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;27const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;28const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;29const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;30const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;3132let balanceUniqueTokenInit: bigint;33let balanceUniqueTokenMiddle: bigint;34let balanceUniqueTokenFinal: bigint;35let unqFees: bigint;3637const testHelper = new XcmTestHelper('unique');24const testHelper = new XcmTestHelper('unique');382539async function genericSendUnqTo(40 networkName: keyof typeof NETWORKS,41 randomAccount: IKeyringPair,42 randomAccountOnTargetChain = randomAccount,43) {44 const networkUrl = mapToChainUrl(networkName);45 const targetPlayground = getDevPlayground(networkName);46 await usingPlaygrounds(async (helper) => {47 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);48 const destination = {49 V2: {50 parents: 1,51 interior: {52 X1: {53 Parachain: mapToChainId(networkName),54 },55 },56 },57 };582659 const beneficiary = {60 V2: {61 parents: 0,62 interior: {63 X1: (64 networkName == 'moonbeam' ?65 {66 AccountKey20: {67 network: 'Any',68 key: randomAccountOnTargetChain.address,69 },70 }71 :72 {73 AccountId32: {74 network: 'Any',75 id: randomAccountOnTargetChain.addressRaw,76 },77 }78 ),79 },80 },81 };8283 const assets = {84 V2: [85 {86 id: {87 Concrete: {88 parents: 0,89 interior: 'Here',90 },91 },92 fun: {93 Fungible: TRANSFER_AMOUNT,94 },95 },96 ],97 };98 const feeAssetItem = 0;99100 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');101 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);102 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);103104 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;105 console.log('[Unique -> %s] transaction fees on Unique: %s UNQ', networkName, helper.util.bigIntToDecimals(unqFees));106 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;107108 await targetPlayground(networkUrl, async (helper) => {109 /*110 Since only the parachain part of the Polkadex111 infrastructure is launched (without their112 solochain validators), processing incoming113 assets will lead to an error.114 This error indicates that the Polkadex chain115 received a message from the Unique network,116 since the hash is being checked to ensure117 it matches what was sent.118 */119 if(networkName == 'polkadex') {120 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);121 } else {122 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash);123 }124 });125126 });127}128129async function genericSendUnqBack(130 networkName: keyof typeof NETWORKS,131 sudoer: IKeyringPair,132 randomAccountOnUnq: IKeyringPair,133) {134 const networkUrl = mapToChainUrl(networkName);135136 const targetPlayground = getDevPlayground(networkName);137 await usingPlaygrounds(async (helper) => {138139 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(140 randomAccountOnUnq.addressRaw,141 uniqueAssetId,142 SENDBACK_AMOUNT,143 );144145 let xcmProgramSent: any;146147148 await targetPlayground(networkUrl, async (helper) => {149 if('getSudo' in helper) {150 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, xcmProgram);151 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);152 } else if('fastDemocracy' in helper) {153 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, xcmProgram]);154 // Needed to bypass the call filter.155 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);156 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);157 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);158 }159 });160161 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);162163 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address);164165 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees - STAYED_ON_TARGET_CHAIN);166167 });168}169170async function genericSendOnlyOwnedBalance(171 networkName: keyof typeof NETWORKS,172 sudoer: IKeyringPair,173) {174 const networkUrl = mapToChainUrl(networkName);175 const targetPlayground = getDevPlayground(networkName);176177 const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);178179 await usingPlaygrounds(async (helper) => {180 const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName));181 await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance);182 const moreThanTargetChainHas = 2n * targetChainBalance;183184 const targetAccount = helper.arrange.createEmptyAccount();185186 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(187 targetAccount.addressRaw,188 {189 Concrete: {190 parents: 0,191 interior: 'Here',192 },193 },194 moreThanTargetChainHas,195 );196197 let maliciousXcmProgramSent: any;198199200 await targetPlayground(networkUrl, async (helper) => {201 if('getSudo' in helper) {202 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgram);203 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);204 } else if('fastDemocracy' in helper) {205 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);206 // Needed to bypass the call filter.207 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);208 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);209 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);210 }211 });212213 await expectFailedToTransact(helper, maliciousXcmProgramSent);214215 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);216 expect(targetAccountBalance).to.be.equal(0n);217 });218}219220async function genericReserveTransferUNQfrom(netwokrName: keyof typeof NETWORKS, sudoer: IKeyringPair) {221 const networkUrl = mapToChainUrl(netwokrName);222 const targetPlayground = getDevPlayground(netwokrName);223224 await usingPlaygrounds(async (helper) => {225 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);226 const targetAccount = helper.arrange.createEmptyAccount();227228 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(229 targetAccount.addressRaw,230 uniqueAssetId,231 testAmount,232 );233234 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(235 targetAccount.addressRaw,236 {237 Concrete: {238 parents: 0,239 interior: 'Here',240 },241 },242 testAmount,243 );244245 let maliciousXcmProgramFullIdSent: any;246 let maliciousXcmProgramHereIdSent: any;247 const maxWaitBlocks = 3;248249 // Try to trick Unique using full UNQ identification250 await targetPlayground(networkUrl, async (helper) => {251 if('getSudo' in helper) {252 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgramFullId);253 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);254 }255 // Moonbeam case256 else if('fastDemocracy' in helper) {257 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);258 // Needed to bypass the call filter.259 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);260 await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using path asset identification`,batchCall);261262 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);263 }264 });265266267 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);268269 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);270 expect(accountBalance).to.be.equal(0n);271272 // Try to trick Unique using shortened UNQ identification273 await targetPlayground(networkUrl, async (helper) => {274 if('getSudo' in helper) {275 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgramHereId);276 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);277 }278 else if('fastDemocracy' in helper) {279 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);280 // Needed to bypass the call filter.281 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);282 await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);283284 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);285 }286 });287288 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);289290 accountBalance = await helper.balance.getSubstrate(targetAccount.address);291 expect(accountBalance).to.be.equal(0n);292 });293}294295async function genericRejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {296 const networkUrl = mapToChainUrl(networkName);297 const targetPlayground = getDevPlayground(networkName);298 let messageSent: any;299300 await usingPlaygrounds(async (helper) => {301 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(302 helper.arrange.createEmptyAccount().addressRaw,303 {304 Concrete: {305 parents: 1,306 interior: {307 X1: {308 Parachain: mapToChainId(networkName),309 },310 },311 },312 },313 TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT,314 );315 await targetPlayground(networkUrl, async (helper) => {316 if('getSudo' in helper) {317 await helper.getSudo().xcm.send(sudoerOnTargetChain, uniqueVersionedMultilocation, maliciousXcmProgramFullId);318 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);319 } else if('fastDemocracy' in helper) {320 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);321 // Needed to bypass the call filter.322 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);323 await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);324325 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);326 }327 });328 await expectFailedToTransact(helper, messageSent);329 });330}3312733228333describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {29describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {37571376 await usingPlaygrounds(async (helper) => {72 await usingPlaygrounds(async (helper) => {377 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);73 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);378 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);379 });74 });380 });75 });38176382 itSub('Should connect and send UNQ to Acala', async () => {77 itSub('Should connect and send UNQ to Acala', async () => {383 await genericSendUnqTo('acala', randomAccount);78 await testHelper.sendUnqTo('acala', randomAccount);384 });79 });38580386 itSub('Should connect to Acala and send UNQ back', async () => {81 itSub('Should connect to Acala and send UNQ back', async () => {387 await genericSendUnqBack('acala', alice, randomAccount);82 await testHelper.sendUnqBack('acala', alice, randomAccount);388 });83 });38984390 itSub('Acala can send only up to its balance', async () => {85 itSub('Acala can send only up to its balance', async () => {391 await genericSendOnlyOwnedBalance('acala', alice);86 await testHelper.sendOnlyOwnedBalance('acala', alice);392 });87 });39388394 itSub('Should not accept reserve transfer of UNQ from Acala', async () => {89 itSub('Should not accept reserve transfer of UNQ from Acala', async () => {395 await genericReserveTransferUNQfrom('acala', alice);90 await testHelper.reserveTransferUNQfrom('acala', alice);396 });91 });397});92});39893430125431 await usingPlaygrounds(async (helper) => {126 await usingPlaygrounds(async (helper) => {432 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);127 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);433 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);434 });128 });435 });129 });436130467 });161 });468162469 itSub('Unique rejects ACA tokens from Acala', async () => {163 itSub('Unique rejects ACA tokens from Acala', async () => {470 await genericRejectNativeTokensFrom('acala', alice);164 await testHelper.rejectNativeTokensFrom('acala', alice);471 });165 });472166473 itSub('Unique rejects GLMR tokens from Moonbeam', async () => {167 itSub('Unique rejects GLMR tokens from Moonbeam', async () => {474 await genericRejectNativeTokensFrom('moonbeam', alice);168 await testHelper.rejectNativeTokensFrom('moonbeam', alice);475 });169 });476170477 itSub('Unique rejects ASTR tokens from Astar', async () => {171 itSub('Unique rejects ASTR tokens from Astar', async () => {478 await genericRejectNativeTokensFrom('astar', alice);172 await testHelper.rejectNativeTokensFrom('astar', alice);479 });173 });480174481 itSub('Unique rejects PDX tokens from Polkadex', async () => {175 itSub('Unique rejects PDX tokens from Polkadex', async () => {482 await genericRejectNativeTokensFrom('polkadex', alice);176 await testHelper.rejectNativeTokensFrom('polkadex', alice);483 });177 });484});178});485179570264571 await usingPlaygrounds(async (helper) => {265 await usingPlaygrounds(async (helper) => {572 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);266 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);573 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);574 });267 });575 });268 });576269657 });350 });658351659 itSub('Should connect and send UNQ to Astar', async () => {352 itSub('Should connect and send UNQ to Astar', async () => {660 await genericSendUnqTo('astar', randomAccount);353 await testHelper.sendUnqTo('astar', randomAccount);661 });354 });662355663 itSub('Should connect to Astar and send UNQ back', async () => {356 itSub('Should connect to Astar and send UNQ back', async () => {664 await genericSendUnqBack('astar', alice, randomAccount);357 await testHelper.sendUnqBack('astar', alice, randomAccount);665 });358 });666359667 itSub('Astar can send only up to its balance', async () => {360 itSub('Astar can send only up to its balance', async () => {668 await genericSendOnlyOwnedBalance('astar', alice);361 await testHelper.sendOnlyOwnedBalance('astar', alice);669 });362 });670363671 itSub('Should not accept reserve transfer of UNQ from Astar', async () => {364 itSub('Should not accept reserve transfer of UNQ from Astar', async () => {672 await genericReserveTransferUNQfrom('astar', alice);365 await testHelper.reserveTransferUNQfrom('astar', alice);673 });366 });674});367});675368tests/src/xcm/xcm.types.tsdiffbeforeafterboth--- a/tests/src/xcm/xcm.types.ts
+++ b/tests/src/xcm/xcm.types.ts
@@ -3,7 +3,6 @@
import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util';
import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
import config from '../config';
-import {blake2AsHex} from '@polkadot/util-crypto';
export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);
@@ -136,10 +135,10 @@
}
export 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;
+export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
+export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
+export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;
+export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;
export class XcmTestHelper {
private _balanceUniqueTokenInit: bigint = 0n;
@@ -163,6 +162,7 @@
return UNIQUE_CHAIN;
}
}
+
private _isAddress20FormatFor(network: NetworkNames) {
switch (network) {
case 'moonbeam':
@@ -173,6 +173,19 @@
}
}
+ private _runtimeVersionedMultilocation() {
+ return {
+ V3: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: this._getNativeId(),
+ },
+ },
+ },
+ };
+ }
+
uniqueChainMultilocationForRelay() {
return {
V3: {
@@ -250,8 +263,8 @@
this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
- console.log('[Unique -> %s] transaction fees on Unique: %s UNQ', networkName, helper.util.bigIntToDecimals(this._unqFees));
- expect(this._unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
+ console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees));
+ expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true;
await targetPlayground(networkUrl, async (helper) => {
/*
@@ -302,10 +315,10 @@
await targetPlayground(networkUrl, async (helper) => {
if('getSudo' in helper) {
- await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, xcmProgram);
+ await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram);
xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
} else if('fastDemocracy' in helper) {
- const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, xcmProgram]);
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]);
// Needed to bypass the call filter.
const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
@@ -354,10 +367,10 @@
await targetPlayground(networkUrl, async (helper) => {
if('getSudo' in helper) {
- await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgram);
+ await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram);
maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
} else if('fastDemocracy' in helper) {
- const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]);
// Needed to bypass the call filter.
const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
@@ -413,12 +426,12 @@
// Try to trick Unique using full UNQ identification
await targetPlayground(networkUrl, async (helper) => {
if('getSudo' in helper) {
- await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgramFullId);
+ await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), 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', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
// Needed to bypass the call filter.
const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using path asset identification`,batchCall);
@@ -436,11 +449,11 @@
// Try to trick Unique using shortened UNQ identification
await targetPlayground(networkUrl, async (helper) => {
if('getSudo' in helper) {
- await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgramHereId);
+ await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId);
maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
}
else if('fastDemocracy' in helper) {
- const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]);
// Needed to bypass the call filter.
const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);
@@ -478,10 +491,10 @@
);
await targetPlayground(networkUrl, async (helper) => {
if('getSudo' in helper) {
- await helper.getSudo().xcm.send(sudoerOnTargetChain, uniqueVersionedMultilocation, maliciousXcmProgramFullId);
+ await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
} else if('fastDemocracy' in helper) {
- const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);
+ const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
// Needed to bypass the call filter.
const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);
tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -18,6 +18,7 @@
import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
import {STATEMINE_CHAIN, QUARTZ_CHAIN, KARURA_CHAIN, MOONRIVER_CHAIN, SHIDEN_CHAIN, STATEMINE_DECIMALS, KARURA_DECIMALS, QTZ_DECIMALS, RELAY_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, relayUrl, shidenUrl, statemineUrl} from './xcm.types';
+import {hexToString} from '@polkadot/util';
@@ -483,7 +484,14 @@
minimalBalance: 1000000000000000000n,
};
- await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
+ hexToString(v.toJSON()['symbol'])) as string[];
+
+ if(!assets.includes('QTZ')) {
+ await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+ } else {
+ console.log('QTZ token already registered on Karura assetRegistry pallet');
+ }
await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);
balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
@@ -969,19 +977,22 @@
const unitsPerSecond = 1n;
const numAssetsWeightHint = 0;
- const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
- location: quartzAssetLocation,
- metadata: quartzAssetMetadata,
- existentialDeposit,
- isSufficient,
- unitsPerSecond,
- numAssetsWeightHint,
- });
-
- console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+ if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
+ console.log('Quartz asset already registered on Moonriver');
+ } else {
+ const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+ location: quartzAssetLocation,
+ metadata: quartzAssetMetadata,
+ existentialDeposit,
+ isSufficient,
+ unitsPerSecond,
+ numAssetsWeightHint,
+ });
- await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
+ console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+ await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
+ }
// >>> Acquire Quartz AssetId Info on Moonriver >>>
console.log('Acquire Quartz AssetId Info on Moonriver.......');
@@ -1298,40 +1309,43 @@
});
await usingShidenPlaygrounds(shidenUrl, async (helper) => {
- console.log('1. Create foreign asset and metadata');
- // TODO update metadata with values from production
- await helper.assets.create(
- alice,
- QTZ_ASSET_ID_ON_SHIDEN,
- alice.address,
- QTZ_MINIMAL_BALANCE_ON_SHIDEN,
- );
+ if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
+ console.log('1. Create foreign asset and metadata');
+ // TODO update metadata with values from production
+ await helper.assets.create(
+ alice,
+ QTZ_ASSET_ID_ON_SHIDEN,
+ alice.address,
+ QTZ_MINIMAL_BALANCE_ON_SHIDEN,
+ );
- await helper.assets.setMetadata(
- alice,
- QTZ_ASSET_ID_ON_SHIDEN,
- 'Cross chain QTZ',
- 'xcQTZ',
- Number(QTZ_DECIMALS),
- );
+ await helper.assets.setMetadata(
+ alice,
+ QTZ_ASSET_ID_ON_SHIDEN,
+ 'Cross chain QTZ',
+ 'xcQTZ',
+ Number(QTZ_DECIMALS),
+ );
- console.log('2. Register asset location on Shiden');
- const assetLocation = {
- V2: {
- parents: 1,
- interior: {
- X1: {
- Parachain: QUARTZ_CHAIN,
+ console.log('2. Register asset location on Shiden');
+ const assetLocation = {
+ V2: {
+ parents: 1,
+ interior: {
+ X1: {
+ Parachain: QUARTZ_CHAIN,
+ },
},
},
- },
- };
+ };
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
-
- console.log('3. Set QTZ payment for XCM execution on Shiden');
- await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
+ console.log('3. Set QTZ payment for XCM execution on Shiden');
+ await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+ } else {
+ console.log('QTZ is already registered on Shiden');
+ }
console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);
});