git.delta.rocks / unique-network / refs/commits / 668280b1206d

difftreelog

feat(xcm tests) `XcmHelper` integraion for QTZ\UNQ

PraetorP2023-09-29parent: #06b395f.patch.diff
in: master

6 files changed

modified.baedeker/.gitignorediffbeforeafterboth
1/.bdk-env1/.bdk-env
2/rewrites.jsonnet2/rewrites*.jsonnet
3/vendor3/vendor
4/baedeker-library4/baedeker-library
55!/rewrites.example.jsonnet
modified.github/workflows/xcm.ymldiffbeforeafterboth
36 uses: CertainLach/create-matrix-action@v436 uses: CertainLach/create-matrix-action@v4
37 id: create_matrix37 id: create_matrix
38 with:38 with:
39 matrix: |39 matrix: |
40 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}40 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}
41 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}41 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}
42 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}42 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}
4343
44 xcm:44 xcm:
45 needs: prepare-execution-marix45 needs: prepare-execution-marix
modifiedtests/src/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util';18import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util';
19import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
20import {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';19import {QUARTZ_CHAIN, QTZ_DECIMALS, SHIDEN_DECIMALS, karuraUrl, moonriverUrl, shidenUrl, SAFE_XCM_VERSION, XcmTestHelper, TRANSFER_AMOUNT, SENDER_BUDGET, relayUrl} from './xcm.types';
2120import {hexToString} from '@polkadot/util';
2221
23const testHelper = new XcmTestHelper('quartz');22const testHelper = new XcmTestHelper('quartz');
2423
25describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {24describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {
26 let alice: IKeyringPair;25 let alice: IKeyringPair;
27 let randomAccount: IKeyringPair;26 let randomAccount: IKeyringPair;
28
29 let balanceQuartzTokenInit: bigint;
30 let balanceQuartzTokenMiddle: bigint;
31 let balanceQuartzTokenFinal: bigint;
32 let balanceKaruraTokenInit: bigint;
33 let balanceKaruraTokenMiddle: bigint;
34 let balanceKaruraTokenFinal: bigint;
35 let balanceQuartzForeignTokenInit: bigint;
36 let balanceQuartzForeignTokenMiddle: bigint;
37 let balanceQuartzForeignTokenFinal: bigint;
38
39 // computed by a test transfer from prod Quartz to prod Karura.
40 // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9
41 // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block)
42 const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;
43 const karuraEps = 8n * 10n ** 16n;
44
45 let karuraBackwardTransferAmount: bigint;
4627
47 before(async () => {28 before(async () => {
48 await usingPlaygrounds(async (helper, privateKey) => {29 await usingPlaygrounds(async (helper, privateKey) => {
72 minimalBalance: 1000000000000000000n,53 minimalBalance: 1000000000000000000n,
73 };54 };
7455
56 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
57 hexToString(v.toJSON()['symbol'])) as string[];
58
59 if(!assets.includes('QTZ')) {
75 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);60 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
61 } else {
62 console.log('QTZ token already registered on Karura assetRegistry pallet');
63 }
76 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);64 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
77 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);
78 balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
79 });65 });
8066
81 await usingPlaygrounds(async (helper) => {67 await usingPlaygrounds(async (helper) => {
82 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);68 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
83 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);
84 });69 });
85 });70 });
8671
100// the the corresponding foreign assets are not registered85// the the corresponding foreign assets are not registered
101describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {86describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {
102 let alice: IKeyringPair;87 let alice: IKeyringPair;
103 let alith: IKeyringPair;88
104
105 const testAmount = 100_000_000_000n;
106 let quartzParachainJunction;
107 let quartzAccountJunction;
108
109 let quartzParachainMultilocation: any;
110 let quartzAccountMultilocation: any;
111 let quartzCombinedMultilocation: any;
112
113 let messageSent: any;
114
115 const maxWaitBlocks = 3;
11689
117 before(async () => {90 before(async () => {
118 await usingPlaygrounds(async (helper, privateKey) => {91 await usingPlaygrounds(async (helper, privateKey) => {
119 alice = await privateKey('//Alice');92 alice = await privateKey('//Alice');
12093
121 quartzParachainJunction = {Parachain: QUARTZ_CHAIN};94
122 quartzAccountJunction = {
123 AccountId32: {
124 network: 'Any',
125 id: alice.addressRaw,
126 },
127 };
128
129 quartzParachainMultilocation = {
130 V2: {
131 parents: 1,
132 interior: {
133 X1: quartzParachainJunction,
134 },
135 },
136 };
137
138 quartzAccountMultilocation = {
139 V2: {
140 parents: 0,
141 interior: {
142 X1: quartzAccountJunction,
143 },
144 },
145 };
146
147 quartzCombinedMultilocation = {
148 V2: {
149 parents: 1,
150 interior: {
151 X2: [quartzParachainJunction, quartzAccountJunction],
152 },
153 },
154 };
15595
156 // Set the default version to wrap the first message to other chains.96 // Set the default version to wrap the first message to other chains.
157 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);97 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
158 });98 });
159
160 // eslint-disable-next-line require-await
161 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
162 alith = helper.account.alithAccount();
163 });
164 });99 });
165100
166 itSub('Quartz rejects KAR tokens from Karura', async () => {101 itSub('Quartz rejects KAR tokens from Karura', async () => {
195 minimalBalance: 1n,130 minimalBalance: 1n,
196 };131 };
197132
198 let balanceQuartzTokenInit: bigint;
199 let balanceQuartzTokenMiddle: bigint;
200 let balanceQuartzTokenFinal: bigint;
201 let balanceForeignQtzTokenInit: bigint;
202 let balanceForeignQtzTokenMiddle: bigint;
203 let balanceForeignQtzTokenFinal: bigint;
204 let balanceMovrTokenInit: bigint;
205 let balanceMovrTokenMiddle: bigint;
206 let balanceMovrTokenFinal: bigint;
207133
208 before(async () => {134 before(async () => {
209 await usingPlaygrounds(async (helper, privateKey) => {135 await usingPlaygrounds(async (helper, privateKey) => {
210 alice = await privateKey('//Alice');136 alice = await privateKey('//Alice');
211 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);137 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
212138
213 balanceForeignQtzTokenInit = 0n;
214139
215 // Set the default version to wrap the first message to other chains.140 // Set the default version to wrap the first message to other chains.
216 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);141 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
239 const isSufficient = true;164 const isSufficient = true;
240 const unitsPerSecond = 1n;165 const unitsPerSecond = 1n;
241 const numAssetsWeightHint = 0;166 const numAssetsWeightHint = 0;
242167 if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
168 console.log('Quartz asset already registered on Moonriver');
169 } else {
243 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({170 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
244 location: quartzAssetLocation,171 location: quartzAssetLocation,
245 metadata: quartzAssetMetadata,172 metadata: quartzAssetMetadata,
252 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);179 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
253180
254 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);181 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
255182 }
256 // >>> Acquire Quartz AssetId Info on Moonriver >>>183 // >>> Acquire Quartz AssetId Info on Moonriver >>>
257 console.log('Acquire Quartz AssetId Info on Moonriver.......');184 console.log('Acquire Quartz AssetId Info on Moonriver.......');
258185
268 console.log('Sponsoring random Account.......DONE');195 console.log('Sponsoring random Account.......DONE');
269 // <<< Sponsoring random Account <<<196 // <<< Sponsoring random Account <<<
270
271 balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);
272 });197 });
273198
274 await usingPlaygrounds(async (helper) => {199 await usingPlaygrounds(async (helper) => {
275 await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);200 await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
276 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);
277 });201 });
278 });202 });
279203
296220
297describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {221describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {
298 let alice: IKeyringPair;222 let alice: IKeyringPair;
299 let sender: IKeyringPair;223 let randomAccount: IKeyringPair;
300224
301 const QTZ_ASSET_ID_ON_SHIDEN = 1;225 const QTZ_ASSET_ID_ON_SHIDEN = 1;
302 const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n;226 const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n;
303227
304 // Quartz -> Shiden228 // Quartz -> Shiden
305 const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden229 const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
306 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?230 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?
307 const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ231
308 const qtzToShidenArrived = 9_999_999_999_088_000_000n; // 9.999 ... QTZ, Shiden takes a commision in foreign tokens232
309
310 // Shiden -> Quartz
311 const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ
312 const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 4.999_999_999_088_000_000n QTZ
313
314 let balanceAfterQuartzToShidenXCM: bigint;
315233
316 before(async () => {234 before(async () => {
317 await usingPlaygrounds(async (helper, privateKey) => {235 await usingPlaygrounds(async (helper, privateKey) => {
318 alice = await privateKey('//Alice');236 alice = await privateKey('//Alice');
319 [sender] = await helper.arrange.createAccounts([100n], alice);237 randomAccount = helper.arrange.createEmptyAccount();
238 await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
320 console.log('sender', sender.address);239 console.log('sender: ', randomAccount.address);
321240
322 // Set the default version to wrap the first message to other chains.241 // Set the default version to wrap the first message to other chains.
323 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);242 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
324 });243 });
325244
326 await usingShidenPlaygrounds(shidenUrl, async (helper) => {245 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
246 if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
327 console.log('1. Create foreign asset and metadata');247 console.log('1. Create foreign asset and metadata');
328 // TODO update metadata with values from production248 // TODO update metadata with values from production
329 await helper.assets.create(249 await helper.assets.create(
357277
358 console.log('3. Set QTZ payment for XCM execution on Shiden');278 console.log('3. Set QTZ payment for XCM execution on Shiden');
359 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);279 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
360280 } else {
281 console.log('QTZ is already registered on Shiden');
282 }
361 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');283 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
362 await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);284 await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);
363 });285 });
364 });286 });
365287
366 itSub('Should connect and send QTZ to Shiden', async () => {288 itSub('Should connect and send QTZ to Shiden', async () => {
367 await testHelper.sendUnqTo('shiden', sender);289 await testHelper.sendUnqTo('shiden', randomAccount);
368 });290 });
369291
370 itSub('Should connect to Shiden and send QTZ back', async () => {292 itSub('Should connect to Shiden and send QTZ back', async () => {
371 await testHelper.sendUnqBack('shiden', alice, sender);293 await testHelper.sendUnqBack('shiden', alice, randomAccount);
372 });294 });
373295
374 itSub('Shiden can send only up to its balance', async () => {296 itSub('Shiden can send only up to its balance', async () => {
modifiedtests/src/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth
1616
17import {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';
2423
25
26const 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;
31
32let balanceUniqueTokenInit: bigint;
33let balanceUniqueTokenMiddle: bigint;
34let balanceUniqueTokenFinal: bigint;
35let unqFees: bigint;
36
37const testHelper = new XcmTestHelper('unique');24const testHelper = new XcmTestHelper('unique');
3825
39async 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 };
5826
59 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 };
82
83 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;
99
100 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);
103
104 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;
107
108 await targetPlayground(networkUrl, async (helper) => {
109 /*
110 Since only the parachain part of the Polkadex
111 infrastructure is launched (without their
112 solochain validators), processing incoming
113 assets will lead to an error.
114 This error indicates that the Polkadex chain
115 received a message from the Unique network,
116 since the hash is being checked to ensure
117 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 });
125
126 });
127}
128
129async function genericSendUnqBack(
130 networkName: keyof typeof NETWORKS,
131 sudoer: IKeyringPair,
132 randomAccountOnUnq: IKeyringPair,
133) {
134 const networkUrl = mapToChainUrl(networkName);
135
136 const targetPlayground = getDevPlayground(networkName);
137 await usingPlaygrounds(async (helper) => {
138
139 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
140 randomAccountOnUnq.addressRaw,
141 uniqueAssetId,
142 SENDBACK_AMOUNT,
143 );
144
145 let xcmProgramSent: any;
146
147
148 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 });
160
161 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);
162
163 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address);
164
165 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees - STAYED_ON_TARGET_CHAIN);
166
167 });
168}
169
170async function genericSendOnlyOwnedBalance(
171 networkName: keyof typeof NETWORKS,
172 sudoer: IKeyringPair,
173) {
174 const networkUrl = mapToChainUrl(networkName);
175 const targetPlayground = getDevPlayground(networkName);
176
177 const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);
178
179 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;
183
184 const targetAccount = helper.arrange.createEmptyAccount();
185
186 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
187 targetAccount.addressRaw,
188 {
189 Concrete: {
190 parents: 0,
191 interior: 'Here',
192 },
193 },
194 moreThanTargetChainHas,
195 );
196
197 let maliciousXcmProgramSent: any;
198
199
200 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 });
212
213 await expectFailedToTransact(helper, maliciousXcmProgramSent);
214
215 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
216 expect(targetAccountBalance).to.be.equal(0n);
217 });
218}
219
220async function genericReserveTransferUNQfrom(netwokrName: keyof typeof NETWORKS, sudoer: IKeyringPair) {
221 const networkUrl = mapToChainUrl(netwokrName);
222 const targetPlayground = getDevPlayground(netwokrName);
223
224 await usingPlaygrounds(async (helper) => {
225 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
226 const targetAccount = helper.arrange.createEmptyAccount();
227
228 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
229 targetAccount.addressRaw,
230 uniqueAssetId,
231 testAmount,
232 );
233
234 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
235 targetAccount.addressRaw,
236 {
237 Concrete: {
238 parents: 0,
239 interior: 'Here',
240 },
241 },
242 testAmount,
243 );
244
245 let maliciousXcmProgramFullIdSent: any;
246 let maliciousXcmProgramHereIdSent: any;
247 const maxWaitBlocks = 3;
248
249 // Try to trick Unique using full UNQ identification
250 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 case
256 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);
261
262 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
263 }
264 });
265
266
267 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);
268
269 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
270 expect(accountBalance).to.be.equal(0n);
271
272 // Try to trick Unique using shortened UNQ identification
273 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);
283
284 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
285 }
286 });
287
288 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);
289
290 accountBalance = await helper.balance.getSubstrate(targetAccount.address);
291 expect(accountBalance).to.be.equal(0n);
292 });
293}
294
295async function genericRejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {
296 const networkUrl = mapToChainUrl(networkName);
297 const targetPlayground = getDevPlayground(networkName);
298 let messageSent: any;
299
300 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);
324
325 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
326 }
327 });
328 await expectFailedToTransact(helper, messageSent);
329 });
330}
33127
33228
333describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {29describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {
37571
376 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 });
38176
382 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 });
38580
386 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 });
38984
390 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 });
39388
394 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});
39893
430125
431 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 });
436130
467 });161 });
468162
469 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 });
472166
473 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 });
476170
477 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 });
480174
481 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});
485179
570264
571 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 });
576269
657 });350 });
658351
659 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 });
662355
663 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 });
666359
667 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 });
670363
671 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});
675368
modifiedtests/src/xcm/xcm.types.tsdiffbeforeafterboth
3import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util';3import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util';
4import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';4import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
5import config from '../config';5import config from '../config';
6import {blake2AsHex} from '@polkadot/util-crypto';
76
8export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);7export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
9export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);8export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);
136}135}
137136
138export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;137export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;
139const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;138export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
140const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;139export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
141const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;140export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;
142const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;141export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;
143142
144export class XcmTestHelper {143export class XcmTestHelper {
145 private _balanceUniqueTokenInit: bigint = 0n;144 private _balanceUniqueTokenInit: bigint = 0n;
173 }173 }
174 }174 }
175
176 private _runtimeVersionedMultilocation() {
177 return {
178 V3: {
179 parents: 1,
180 interior: {
181 X1: {
182 Parachain: this._getNativeId(),
183 },
184 },
185 },
186 };
187 }
175188
176 uniqueChainMultilocationForRelay() {189 uniqueChainMultilocationForRelay() {
177 return {190 return {
250 this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);263 this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
251264
252 this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT;265 this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
253 console.log('[Unique -> %s] transaction fees on Unique: %s UNQ', networkName, helper.util.bigIntToDecimals(this._unqFees));266 console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees));
254 expect(this._unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;267 expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true;
255268
256 await targetPlayground(networkUrl, async (helper) => {269 await targetPlayground(networkUrl, async (helper) => {
257 /*270 /*
302315
303 await targetPlayground(networkUrl, async (helper) => {316 await targetPlayground(networkUrl, async (helper) => {
304 if('getSudo' in helper) {317 if('getSudo' in helper) {
305 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, xcmProgram);318 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram);
306 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);319 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
307 } else if('fastDemocracy' in helper) {320 } else if('fastDemocracy' in helper) {
308 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, xcmProgram]);321 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]);
309 // Needed to bypass the call filter.322 // Needed to bypass the call filter.
310 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);323 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
311 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);324 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
354367
355 await targetPlayground(networkUrl, async (helper) => {368 await targetPlayground(networkUrl, async (helper) => {
356 if('getSudo' in helper) {369 if('getSudo' in helper) {
357 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgram);370 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram);
358 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);371 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
359 } else if('fastDemocracy' in helper) {372 } else if('fastDemocracy' in helper) {
360 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);373 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]);
361 // Needed to bypass the call filter.374 // Needed to bypass the call filter.
362 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);375 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
363 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);376 await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
413 // Try to trick Unique using full UNQ identification426 // Try to trick Unique using full UNQ identification
414 await targetPlayground(networkUrl, async (helper) => {427 await targetPlayground(networkUrl, async (helper) => {
415 if('getSudo' in helper) {428 if('getSudo' in helper) {
416 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgramFullId);429 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
417 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);430 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
418 }431 }
419 // Moonbeam case432 // Moonbeam case
420 else if('fastDemocracy' in helper) {433 else if('fastDemocracy' in helper) {
421 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);434 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
422 // Needed to bypass the call filter.435 // Needed to bypass the call filter.
423 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);436 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
424 await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using path asset identification`,batchCall);437 await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using path asset identification`,batchCall);
436 // Try to trick Unique using shortened UNQ identification449 // Try to trick Unique using shortened UNQ identification
437 await targetPlayground(networkUrl, async (helper) => {450 await targetPlayground(networkUrl, async (helper) => {
438 if('getSudo' in helper) {451 if('getSudo' in helper) {
439 await helper.getSudo().xcm.send(sudoer, uniqueVersionedMultilocation, maliciousXcmProgramHereId);452 await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId);
440 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);453 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
441 }454 }
442 else if('fastDemocracy' in helper) {455 else if('fastDemocracy' in helper) {
443 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);456 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]);
444 // Needed to bypass the call filter.457 // Needed to bypass the call filter.
445 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);458 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
446 await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);459 await helper.fastDemocracy.executeProposal(`${netwokrName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);
478 );491 );
479 await targetPlayground(networkUrl, async (helper) => {492 await targetPlayground(networkUrl, async (helper) => {
480 if('getSudo' in helper) {493 if('getSudo' in helper) {
481 await helper.getSudo().xcm.send(sudoerOnTargetChain, uniqueVersionedMultilocation, maliciousXcmProgramFullId);494 await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
482 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);495 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
483 } else if('fastDemocracy' in helper) {496 } else if('fastDemocracy' in helper) {
484 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);497 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
485 // Needed to bypass the call filter.498 // Needed to bypass the call filter.
486 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);499 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
487 await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);500 await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
18import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';18import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';
19import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';19import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';
20import {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';20import {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';
21import {hexToString} from '@polkadot/util';
2122
2223
2324
483 minimalBalance: 1000000000000000000n,484 minimalBalance: 1000000000000000000n,
484 };485 };
485486
487 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
488 hexToString(v.toJSON()['symbol'])) as string[];
489
490 if(!assets.includes('QTZ')) {
486 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);491 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
492 } else {
493 console.log('QTZ token already registered on Karura assetRegistry pallet');
494 }
487 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);495 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
488 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);496 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);
489 balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});497 balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
969 const unitsPerSecond = 1n;977 const unitsPerSecond = 1n;
970 const numAssetsWeightHint = 0;978 const numAssetsWeightHint = 0;
971979
980 if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
981 console.log('Quartz asset already registered on Moonriver');
982 } else {
972 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({983 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
973 location: quartzAssetLocation,984 location: quartzAssetLocation,
974 metadata: quartzAssetMetadata,985 metadata: quartzAssetMetadata,
981 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);992 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
982993
983 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);994 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
984995 }
985 // >>> Acquire Quartz AssetId Info on Moonriver >>>996 // >>> Acquire Quartz AssetId Info on Moonriver >>>
986 console.log('Acquire Quartz AssetId Info on Moonriver.......');997 console.log('Acquire Quartz AssetId Info on Moonriver.......');
987998
1298 });1309 });
12991310
1300 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1311 await usingShidenPlaygrounds(shidenUrl, async (helper) => {
1312 if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
1301 console.log('1. Create foreign asset and metadata');1313 console.log('1. Create foreign asset and metadata');
1302 // TODO update metadata with values from production1314 // TODO update metadata with values from production
1303 await helper.assets.create(1315 await helper.assets.create(
13311343
1332 console.log('3. Set QTZ payment for XCM execution on Shiden');1344 console.log('3. Set QTZ payment for XCM execution on Shiden');
1333 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);1345 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
13341346 } else {
1347 console.log('QTZ is already registered on Shiden');
1348 }
1335 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');1349 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
1336 await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);1350 await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);
1337 });1351 });