git.delta.rocks / unique-network / refs/commits / 503028ebb65c

difftreelog

test(xcm) qtz/unq rejects relay tokens

Daniel Shiposha2022-09-06parent: #6c9f876.patch.diff
in: master

3 files changed

modifiedtests/src/util/helpers.tsdiffbeforeafterboth
104}104}
105105
106export function bigIntToDecimals(number: bigint, decimals = 18): string {106export function bigIntToDecimals(number: bigint, decimals = 18): string {
107 let numberStr = number.toString();107 const numberStr = number.toString();
108 console.log('[0] str = ', numberStr);
109
110 // Get rid of `n` at the end
111 numberStr = numberStr.substring(0, numberStr.length - 1);
112 console.log('[1] str = ', numberStr);
113
114 const dotPos = numberStr.length - decimals;108 const dotPos = numberStr.length - decimals;
109
1794): Promise<EventRecord | null> {1789): Promise<EventRecord | null> {
17951790
1796 const promise = new Promise<EventRecord | null>(async (resolve) => {1791 const promise = new Promise<EventRecord | null>(async (resolve) => {
1797 const unsubscribe = await api.query.system.events(eventRecords => {1792 const unsubscribe = await api.rpc.chain.subscribeNewHeads(async header => {
1793 const blockNumber = header.number.toHuman();
1794 const blockHash = header.hash;
1795 const eventIdStr = `${eventSection}.${eventMethod}`;
1796 const waitLimitStr = `wait blocks remaining: ${maxBlocksToWait}`;
1797
1798 console.log(`[Block #${blockNumber}] Waiting for event \`${eventIdStr}\` (${waitLimitStr})`);
1799
1800 const apiAt = await api.at(blockHash);
1801 const eventRecords = await apiAt.query.system.events();
1802
1798 const neededEvent = eventRecords.find(r => {1803 const neededEvent = eventRecords.find(r => {
1799 return r.event.section == eventSection && r.event.method == eventMethod;1804 return r.event.section == eventSection && r.event.method == eventMethod;
1800 });1805 });
18011806
1802 if (neededEvent) {1807 if (neededEvent) {
1803 unsubscribe();1808 console.log(`Event \`${eventIdStr}\` is found`);
1804 resolve(neededEvent);1809
1805 }1810 unsubscribe();
18061811 resolve(neededEvent);
1807 if (maxBlocksToWait > 0) {1812 } else if (maxBlocksToWait > 0) {
1808 maxBlocksToWait--;1813 maxBlocksToWait--;
1809 } else {1814 } else {
1815 console.log(`Event \`${eventIdStr}\` is NOT found`);
1816
1810 unsubscribe();1817 unsubscribe();
1811 resolve(null);1818 resolve(null);
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
26import {blake2AsHex} from '@polkadot/util-crypto';26import {blake2AsHex} from '@polkadot/util-crypto';
27import waitNewBlocks from '../substrate/wait-new-blocks';27import waitNewBlocks from '../substrate/wait-new-blocks';
28import getBalance from '../substrate/get-balance';28import getBalance from '../substrate/get-balance';
29import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';
2930
30chai.use(chaiAsPromised);31chai.use(chaiAsPromised);
31const expect = chai.expect;32const expect = chai.expect;
34const KARURA_CHAIN = 2000;35const KARURA_CHAIN = 2000;
35const MOONRIVER_CHAIN = 2023;36const MOONRIVER_CHAIN = 2023;
3637
38const RELAY_PORT = 9844;
37const KARURA_PORT = 9946;39const KARURA_PORT = 9946;
38const MOONRIVER_PORT = 9947;40const MOONRIVER_PORT = 9947;
3941
55 return parachainApiOptions(MOONRIVER_PORT);57 return parachainApiOptions(MOONRIVER_PORT);
56}58}
5759
60function relayOptions(): ApiOptions {
61 return parachainApiOptions(RELAY_PORT);
62}
63
58describe_xcm('Integration test: Exchanging tokens with Karura', () => {64describe_xcm('Integration test: Exchanging tokens with Karura', () => {
59 let alice: IKeyringPair;65 let alice: IKeyringPair;
60 let randomAccount: IKeyringPair;66 let randomAccount: IKeyringPair;
200 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;206 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;
201 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;207 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;
202208
203 console.log('209 console.log(
204 [Quartz -> Karura] transaction fees on Karura: %s KAR',210 '[Quartz -> Karura] transaction fees on Karura: %s KAR',
205 bigIntToDecimals(karFees, KARURA_DECIMALS),211 bigIntToDecimals(karFees, KARURA_DECIMALS),
206 );212 );
207 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));213 console.log('[Quartz -> Karura] income %s QTZ', bigIntToDecimals(qtzIncomeTransfer));
281 expect(qtzFees == 0n).to.be.true;287 expect(qtzFees == 0n).to.be.true;
282 });288 });
283 });289 });
290});
284291
285 it('Quartz rejects KAR tokens from Karura', async () => {292// These tests are relevant only when the foreign asset pallet is disabled
293describe('Integration test: Quartz rejects non-native tokens', () => {
286 // This test is relevant only when the foreign asset pallet is disabled294 let alice: IKeyringPair;
287295
296 before(async () => {
297 await usingApi(async (api, privateKeyWrapper) => {
298 alice = privateKeyWrapper('//Alice');
299 });
300 });
301
302 it('Quartz rejects tokens from the Relay', async () => {
288 await usingApi(async (api) => {303 await usingApi(async (api) => {
289 const destination = {304 const destination = {
290 V1: {305 V1: {
306 parents: 0,
307 interior: {X1: {
308 Parachain: QUARTZ_CHAIN,
309 },
310 },
311 }};
312
313 const beneficiary = {
314 V1: {
315 parents: 0,
316 interior: {X1: {
317 AccountId32: {
318 network: 'Any',
319 id: alice.addressRaw,
320 },
321 }},
322 },
323 };
324
325 const assets = {
326 V1: [
327 {
328 id: {
329 Concrete: {
330 parents: 0,
331 interior: 'Here',
332 },
333 },
334 fun: {
335 Fungible: 50_000_000_000_000_000n,
336 },
337 },
338 ],
339 };
340
341 const feeAssetItem = 0;
342
343 const weightLimit = {
344 Limited: 5_000_000_000,
345 };
346
347 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);
348 const events = await submitTransactionAsync(alice, tx);
349 const result = getGenericResult(events);
350 expect(result.success).to.be.true;
351 }, relayOptions());
352
353 await usingApi(async api => {
354 const maxWaitBlocks = 3;
355 const dmpQueueExecutedDownward = await waitEvent(
356 api,
357 maxWaitBlocks,
358 'dmpQueue',
359 'ExecutedDownward',
360 );
361
362 expect(
363 dmpQueueExecutedDownward != null,
364 '[Relay] dmpQueue.ExecutedDownward event is expected',
365 ).to.be.true;
366
367 const event = dmpQueueExecutedDownward!.event;
368 const outcome = event.data[1] as XcmV2TraitsOutcome;
369
370 expect(
371 outcome.isIncomplete,
372 '[Relay] The outcome of the XCM should be `Incomplete`',
373 ).to.be.true;
374
375 const incomplete = outcome.asIncomplete;
376 expect(
377 incomplete[1].toString() == 'AssetNotFound',
378 '[Relay] The XCM error should be `AssetNotFound`',
379 ).to.be.true;
380 });
381 });
382
383 it('Quartz rejects KAR tokens from Karura', async () => {
384 await usingApi(async (api) => {
385 const destination = {
386 V1: {
291 parents: 1,387 parents: 1,
292 interior: {388 interior: {
293 X2: [389 X2: [
294 {Parachain: QUARTZ_CHAIN},390 {Parachain: QUARTZ_CHAIN},
295 {391 {
296 AccountId32: {392 AccountId32: {
297 network: 'Any',393 network: 'Any',
298 id: randomAccount.addressRaw,394 id: alice.addressRaw,
299 },395 },
300 },396 },
301 ],397 ],
321417
322 expect(418 expect(
323 xcmpQueueFailEvent != null,419 xcmpQueueFailEvent != null,
324 'Only native token is supported when the Foreign-Assets pallet is not connected',420 '[Karura] xcmpQueue.FailEvent event is expected',
421 ).to.be.true;
422
423 const event = xcmpQueueFailEvent!.event;
424 const outcome = event.data[1] as XcmV2TraitsError;
425
426 expect(
427 outcome.isUntrustedReserveLocation,
428 '[Karura] The XCM error should be `UntrustedReserveLocation`',
325 ).to.be.true;429 ).to.be.true;
326 });430 });
327 });431 });
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
26import {blake2AsHex} from '@polkadot/util-crypto';26import {blake2AsHex} from '@polkadot/util-crypto';
27import waitNewBlocks from '../substrate/wait-new-blocks';27import waitNewBlocks from '../substrate/wait-new-blocks';
28import getBalance from '../substrate/get-balance';28import getBalance from '../substrate/get-balance';
29import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';
2930
30chai.use(chaiAsPromised);31chai.use(chaiAsPromised);
31const expect = chai.expect;32const expect = chai.expect;
34const ACALA_CHAIN = 2000;35const ACALA_CHAIN = 2000;
35const MOONBEAM_CHAIN = 2004;36const MOONBEAM_CHAIN = 2004;
3637
38const RELAY_PORT = 9844;
37const ACALA_PORT = 9946;39const ACALA_PORT = 9946;
38const MOONBEAM_PORT = 9947;40const MOONBEAM_PORT = 9947;
3941
55 return parachainApiOptions(MOONBEAM_PORT);57 return parachainApiOptions(MOONBEAM_PORT);
56}58}
5759
60function relayOptions(): ApiOptions {
61 return parachainApiOptions(RELAY_PORT);
62}
63
64describe_xcm('Integration test: Exchanging tokens with Acala', () => {
65 let alice: IKeyringPair;
66 let randomAccount: IKeyringPair;
67
68 let balanceUniqueTokenInit: bigint;
69 let balanceUniqueTokenMiddle: bigint;
70 let balanceUniqueTokenFinal: bigint;
71 let balanceAcalaTokenInit: bigint;
72 let balanceAcalaTokenMiddle: bigint;
73 let balanceAcalaTokenFinal: bigint;
74 let balanceUniqueForeignTokenInit: bigint;
75 let balanceUniqueForeignTokenMiddle: bigint;
76 let balanceUniqueForeignTokenFinal: bigint;
77
78 before(async () => {
79 await usingApi(async (api, privateKeyWrapper) => {
80 alice = privateKeyWrapper('//Alice');
81 randomAccount = generateKeyringPair();
82 });
83
84 // Acala side
85 await usingApi(
86 async (api) => {
87 const destination = {
88 V0: {
89 X2: [
90 'Parent',
91 {
92 Parachain: UNIQUE_CHAIN,
93 },
94 ],
95 },
96 };
97
98 const metadata = {
99 name: 'UNQ',
100 symbol: 'UNQ',
101 decimals: 18,
102 minimalBalance: 1,
103 };
104
105 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);
106 const sudoTx = api.tx.sudo.sudo(tx as any);
107 const events = await submitTransactionAsync(alice, sudoTx);
108 const result = getGenericResult(events);
109 expect(result.success).to.be.true;
110
111 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);
112 const events1 = await submitTransactionAsync(alice, tx1);
113 const result1 = getGenericResult(events1);
114 expect(result1.success).to.be.true;
115
116 [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]);
117 {
118 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;
119 balanceUniqueForeignTokenInit = BigInt(free);
120 }
121 },
122 acalaOptions(),
123 );
124
125 // Unique side
126 await usingApi(async (api) => {
127 const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);
128 const events0 = await submitTransactionAsync(alice, tx0);
129 const result0 = getGenericResult(events0);
130 expect(result0.success).to.be.true;
131
132 [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]);
133 });
134 });
135
136 it('Should connect and send UNQ to Acala', async () => {
137
138 // Unique side
139 await usingApi(async (api) => {
140
141 const destination = {
142 V0: {
143 X2: [
144 'Parent',
145 {
146 Parachain: ACALA_CHAIN,
147 },
148 ],
149 },
150 };
151
152 const beneficiary = {
153 V0: {
154 X1: {
155 AccountId32: {
156 network: 'Any',
157 id: randomAccount.addressRaw,
158 },
159 },
160 },
161 };
162
163 const assets = {
164 V1: [
165 {
166 id: {
167 Concrete: {
168 parents: 0,
169 interior: 'Here',
170 },
171 },
172 fun: {
173 Fungible: TRANSFER_AMOUNT,
174 },
175 },
176 ],
177 };
178
179 const feeAssetItem = 0;
180
181 const weightLimit = {
182 Limited: 5000000000,
183 };
184
185 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);
186 const events = await submitTransactionAsync(randomAccount, tx);
187 const result = getGenericResult(events);
188 expect(result.success).to.be.true;
189
190 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);
191
192 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
193 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
194 expect(unqFees > 0n).to.be.true;
195 });
196
197 // Acala side
198 await usingApi(
199 async (api) => {
200 await waitNewBlocks(api, 3);
201 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;
202 balanceUniqueForeignTokenMiddle = BigInt(free);
203
204 [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]);
205
206 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;
207 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;
208
209 console.log(
210 '[Unique -> Acala] transaction fees on Acala: %s ACA',
211 bigIntToDecimals(acaFees, ACALA_DECIMALS),
212 );
213 console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));
214 expect(acaFees == 0n).to.be.true;
215 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
216 },
217 acalaOptions(),
218 );
219 });
220
221 it('Should connect to Acala and send UNQ back', async () => {
222
223 // Acala side
224 await usingApi(
225 async (api) => {
226 const destination = {
227 V1: {
228 parents: 1,
229 interior: {
230 X2: [
231 {Parachain: UNIQUE_CHAIN},
232 {
233 AccountId32: {
234 network: 'Any',
235 id: randomAccount.addressRaw,
236 },
237 },
238 ],
239 },
240 },
241 };
242
243 const id = {
244 ForeignAsset: 0,
245 };
246
247 const destWeight = 50000000;
248
249 const tx = api.tx.xTokens.transfer(id, TRANSFER_AMOUNT, destination, destWeight);
250 const events = await submitTransactionAsync(randomAccount, tx);
251 const result = getGenericResult(events);
252 expect(result.success).to.be.true;
253
254 [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);
255 {
256 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;
257 balanceUniqueForeignTokenFinal = BigInt(free);
258 }
259
260 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;
261 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;
262
263 console.log(
264 '[Acala -> Unique] transaction fees on Acala: %s ACA',
265 bigIntToDecimals(acaFees, ACALA_DECIMALS),
266 );
267 console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));
268
269 expect(acaFees > 0).to.be.true;
270 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
271 },
272 acalaOptions(),
273 );
274
275 // Unique side
276 await usingApi(async (api) => {
277 await waitNewBlocks(api, 3);
278
279 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);
280 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
281 expect(actuallyDelivered > 0).to.be.true;
282
283 console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));
284
285 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;
286 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
287 expect(unqFees == 0n).to.be.true;
288 });
289 });
290});
291
292// These tests are relevant only when the foreign asset pallet is disabled
58describe_xcm('Integration test: Exchanging tokens with Acala', () => {293describe('Integration test: Unique rejects non-native tokens', () => {
59 let alice: IKeyringPair;294 let alice: IKeyringPair;
60 let randomAccount: IKeyringPair;295
61
62 let balanceUniqueTokenInit: bigint;
63 let balanceUniqueTokenMiddle: bigint;
64 let balanceUniqueTokenFinal: bigint;
65 let balanceAcalaTokenInit: bigint;
66 let balanceAcalaTokenMiddle: bigint;
67 let balanceAcalaTokenFinal: bigint;
68 let balanceUniqueForeignTokenInit: bigint;
69 let balanceUniqueForeignTokenMiddle: bigint;
70 let balanceUniqueForeignTokenFinal: bigint;
71
72 before(async () => {296 before(async () => {
73 await usingApi(async (api, privateKeyWrapper) => {297 await usingApi(async (api, privateKeyWrapper) => {
74 alice = privateKeyWrapper('//Alice');298 alice = privateKeyWrapper('//Alice');
75 randomAccount = generateKeyringPair();
76 });299 });
77
78 // Acala side
79 await usingApi(
80 async (api) => {
81 const destination = {
82 V0: {
83 X2: [
84 'Parent',
85 {
86 Parachain: UNIQUE_CHAIN,
87 },
88 ],
89 },
90 };
91
92 const metadata = {
93 name: 'UNQ',
94 symbol: 'UNQ',
95 decimals: 18,
96 minimalBalance: 1,
97 };
98
99 const tx = api.tx.assetRegistry.registerForeignAsset(destination, metadata);
100 const sudoTx = api.tx.sudo.sudo(tx as any);
101 const events = await submitTransactionAsync(alice, sudoTx);
102 const result = getGenericResult(events);
103 expect(result.success).to.be.true;
104
105 const tx1 = api.tx.balances.transfer(randomAccount.address, 10000000000000n);
106 const events1 = await submitTransactionAsync(alice, tx1);
107 const result1 = getGenericResult(events1);
108 expect(result1.success).to.be.true;
109
110 [balanceAcalaTokenInit] = await getBalance(api, [randomAccount.address]);
111 {
112 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;
113 balanceUniqueForeignTokenInit = BigInt(free);
114 }
115 },
116 acalaOptions(),
117 );
118
119 // Unique side
120 await usingApi(async (api) => {
121 const tx0 = api.tx.balances.transfer(randomAccount.address, 10n * TRANSFER_AMOUNT);
122 const events0 = await submitTransactionAsync(alice, tx0);
123 const result0 = getGenericResult(events0);
124 expect(result0.success).to.be.true;
125
126 [balanceUniqueTokenInit] = await getBalance(api, [randomAccount.address]);
127 });
128 });300 });
129 301
130 it('Should connect and send UNQ to Acala', async () => {302 it('Unique rejects tokens from the Relay', async () => {
131
132 // Unique side
133 await usingApi(async (api) => {303 await usingApi(async (api) => {
134
135 const destination = {304 const destination = {
136 V0: {305 V1: {
306 parents: 0,
137 X2: [307 interior: {X1: {
138 'Parent',
139 {
140 Parachain: ACALA_CHAIN,308 Parachain: UNIQUE_CHAIN,
141 },309 },
142 ],310 },
143 },311 }};
144 };312
145
146 const beneficiary = {313 const beneficiary = {
147 V0: {314 V1: {
315 parents: 0,
148 X1: {316 interior: {X1: {
149 AccountId32: {317 AccountId32: {
150 network: 'Any',318 network: 'Any',
151 id: randomAccount.addressRaw,319 id: alice.addressRaw,
152 },320 },
153 },321 }},
154 },322 },
155 };323 };
156 324
164 },332 },
165 },333 },
166 fun: {334 fun: {
167 Fungible: TRANSFER_AMOUNT,335 Fungible: 50_000_000_000_000_000n,
168 },336 },
169 },337 },
170 ],338 ],
173 const feeAssetItem = 0;341 const feeAssetItem = 0;
174 342
175 const weightLimit = {343 const weightLimit = {
176 Limited: 5000000000,344 Limited: 5_000_000_000,
177 };345 };
178 346
179 const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);347 const tx = api.tx.xcmPallet.limitedReserveTransferAssets(destination, beneficiary, assets, feeAssetItem, weightLimit);
180 const events = await submitTransactionAsync(randomAccount, tx);348 const events = await submitTransactionAsync(alice, tx);
181 const result = getGenericResult(events);349 const result = getGenericResult(events);
182 expect(result.success).to.be.true;350 expect(result.success).to.be.true;
183
184 [balanceUniqueTokenMiddle] = await getBalance(api, [randomAccount.address]);
185
186 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
187 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
188 expect(unqFees > 0n).to.be.true;
189 });351 }, relayOptions());
190 352
191 // Acala side
192 await usingApi(353 await usingApi(async api => {
193 async (api) => {
194 await waitNewBlocks(api, 3);354 const maxWaitBlocks = 3;
195 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;355 const dmpQueueExecutedDownward = await waitEvent(
196 balanceUniqueForeignTokenMiddle = BigInt(free);356 api,
197 357 maxWaitBlocks,
358 'dmpQueue',
359 'ExecutedDownward',
360 );
361
198 [balanceAcalaTokenMiddle] = await getBalance(api, [randomAccount.address]);362 expect(
363 dmpQueueExecutedDownward != null,
364 '[Relay] dmpQueue.ExecutedDownward event is expected',
365 ).to.be.true;
199366
200 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;367 const event = dmpQueueExecutedDownward!.event;
201 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;368 const outcome = event.data[1] as XcmV2TraitsOutcome;
202369
203 console.log(
204 '[Unique -> Acala] transaction fees on Acala: %s ACA',
205 bigIntToDecimals(acaFees, ACALA_DECIMALS),
206 );
207 console.log('[Unique -> Acala] income %s UNQ', bigIntToDecimals(unqIncomeTransfer));
208 expect(acaFees == 0n).to.be.true;370 expect(
371 outcome.isIncomplete,
372 '[Relay] The outcome of the XCM should be `Incomplete`',
373 ).to.be.true;
374
375 const incomplete = outcome.asIncomplete;
209 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;376 expect(
377 incomplete[1].toString() == 'AssetNotFound',
378 '[Relay] The XCM error should be `AssetNotFound`',
379 ).to.be.true;
210 },380 });
211 acalaOptions(),
212 );
213 });381 });
214
215 it('Should connect to Acala and send UNQ back', async () => {
216
217 // Acala side
218 await usingApi(
219 async (api) => {
220 const destination = {
221 V1: {
222 parents: 1,
223 interior: {
224 X2: [
225 {Parachain: UNIQUE_CHAIN},
226 {
227 AccountId32: {
228 network: 'Any',
229 id: randomAccount.addressRaw,
230 },
231 },
232 ],
233 },
234 },
235 };
236
237 const id = {
238 ForeignAsset: 0,
239 };
240
241 const destWeight = 50000000;
242
243 const tx = api.tx.xTokens.transfer(id, TRANSFER_AMOUNT, destination, destWeight);
244 const events = await submitTransactionAsync(randomAccount, tx);
245 const result = getGenericResult(events);
246 expect(result.success).to.be.true;
247
248 [balanceAcalaTokenFinal] = await getBalance(api, [randomAccount.address]);
249 {
250 const {free} = (await api.query.tokens.accounts(randomAccount.addressRaw, {ForeignAsset: 0})).toJSON() as any;
251 balanceUniqueForeignTokenFinal = BigInt(free);
252 }
253
254 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;
255 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;
256
257 console.log(
258 '[Acala -> Unique] transaction fees on Acala: %s ACA',
259 bigIntToDecimals(acaFees, ACALA_DECIMALS),
260 );
261 console.log('[Acala -> Unique] outcome %s UNQ', bigIntToDecimals(unqOutcomeTransfer));
262
263 expect(acaFees > 0).to.be.true;
264 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
265 },
266 acalaOptions(),
267 );
268
269 // Unique side
270 await usingApi(async (api) => {
271 await waitNewBlocks(api, 3);
272
273 [balanceUniqueTokenFinal] = await getBalance(api, [randomAccount.address]);
274 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
275 expect(actuallyDelivered > 0).to.be.true;
276
277 console.log('[Acala -> Unique] actually delivered %s UNQ', bigIntToDecimals(actuallyDelivered));
278
279 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;
280 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', bigIntToDecimals(unqFees));
281 expect(unqFees == 0n).to.be.true;
282 });
283 });
284382
285 it('Unique rejects ACA tokens from Acala', async () => {383 it('Unique rejects ACA tokens from Acala', async () => {
286 // This test is relevant only when the foreign asset pallet is disabled
287
288 await usingApi(async (api) => {384 await usingApi(async (api) => {
289 const destination = {385 const destination = {
295 {391 {
296 AccountId32: {392 AccountId32: {
297 network: 'Any',393 network: 'Any',
298 id: randomAccount.addressRaw,394 id: alice.addressRaw,
299 },395 },
300 },396 },
301 ],397 ],
321417
322 expect(418 expect(
323 xcmpQueueFailEvent != null,419 xcmpQueueFailEvent != null,
324 'Only native token is supported when the Foreign-Assets pallet is not connected',420 '[Acala] xcmpQueue.FailEvent event is expected',
325 ).to.be.true;421 ).to.be.true;
422
423 const event = xcmpQueueFailEvent!.event;
424 const outcome = event.data[1] as XcmV2TraitsError;
425
426 expect(
427 outcome.isUntrustedReserveLocation,
428 '[Acala] The XCM error should be `UntrustedReserveLocation`',
429 ).to.be.true;
326 });430 });
327 });431 });
328});432});