difftreelog
test(xcmUnique) switch to `uniqueAssetId` , typo
in: master
2 files changed
tests/src/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth--- a/tests/src/xcm/lowLevelXcmUnique.test.ts
+++ b/tests/src/xcm/lowLevelXcmUnique.test.ts
@@ -137,14 +137,7 @@
const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
randomAccountOnUnq.addressRaw,
- {
- Concrete: {
- parents: 1,
- interior: {
- X1: {Parachain: UNIQUE_CHAIN},
- },
- },
- },
+ uniqueAssetId,
SENDBACK_AMOUNT,
);
@@ -233,16 +226,7 @@
const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
targetAccount.addressRaw,
- {
- Concrete: {
- parents: 1,
- interior: {
- X1: {
- Parachain: UNIQUE_CHAIN,
- },
- },
- },
- },
+ uniqueAssetId,
testAmount,
);
@@ -307,9 +291,9 @@
});
}
-async function genericRejectNativeToknsFrom(netwokrName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {
- const networkUrl = mapToChainUrl(netwokrName);
- const targetPlayground = getDevPlayground(netwokrName);
+async function genericRejectNativeToknsFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {
+ const networkUrl = mapToChainUrl(networkName);
+ const targetPlayground = getDevPlayground(networkName);
let messageSent: any;
await usingPlaygrounds(async (helper) => {
@@ -320,7 +304,7 @@
parents: 1,
interior: {
X1: {
- Parachain: mapToChainId(netwokrName),
+ Parachain: mapToChainId(networkName),
},
},
},
@@ -335,7 +319,7 @@
const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);
// Needed to bypass the call filter.
const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
- await helper.fastDemocracy.executeProposal(`${netwokrName} sending native tokens to the Unique via fast democracy`, batchCall);
+ await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);
messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
}
tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {Event} from '../util/playgrounds/unique.dev';21import {hexToString, nToBigInt} from '@polkadot/util';22import {ACALA_CHAIN, ASTAR_CHAIN, MOONBEAM_CHAIN, POLKADEX_CHAIN, SAFE_XCM_VERSION, STATEMINT_CHAIN, UNIQUE_CHAIN, expectFailedToTransact, expectUntrustedReserveLocationFail, uniqueAssetId, uniqueVersionedMultilocation} from './xcm.types';232425const STATEMINT_PALLET_INSTANCE = 50;2627const relayUrl = config.relayUrl;28const statemintUrl = config.statemintUrl;29const acalaUrl = config.acalaUrl;30const moonbeamUrl = config.moonbeamUrl;31const astarUrl = config.astarUrl;32const polkadexUrl = config.polkadexUrl;3334const RELAY_DECIMALS = 12;35const STATEMINT_DECIMALS = 12;36const ACALA_DECIMALS = 12;37const ASTAR_DECIMALS = 18n;38const UNQ_DECIMALS = 18n;3940const TRANSFER_AMOUNT = 2000000000000000000000000n;4142const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4344const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4546const USDT_ASSET_ID = 100;47const USDT_ASSET_METADATA_DECIMALS = 18;48const USDT_ASSET_METADATA_NAME = 'USDT';49const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';50const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;51const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5253describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {54 let alice: IKeyringPair;55 let bob: IKeyringPair;5657 let balanceStmnBefore: bigint;58 let balanceStmnAfter: bigint;5960 let balanceUniqueBefore: bigint;61 let balanceUniqueAfter: bigint;62 let balanceUniqueFinal: bigint;6364 let balanceBobBefore: bigint;65 let balanceBobAfter: bigint;66 let balanceBobFinal: bigint;6768 let balanceBobRelayTokenBefore: bigint;69 let balanceBobRelayTokenAfter: bigint;707172 before(async () => {73 await usingPlaygrounds(async (helper, privateKey) => {74 alice = await privateKey('//Alice');75 bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor7677 // Set the default version to wrap the first message to other chains.78 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);79 });8081 await usingRelayPlaygrounds(relayUrl, async (helper) => {82 // Fund accounts on Statemint83 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);84 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);85 });8687 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {88 const sovereignFundingAmount = 3_500_000_000n;8990 await helper.assets.create(91 alice,92 USDT_ASSET_ID,93 alice.address,94 USDT_ASSET_METADATA_MINIMAL_BALANCE,95 );96 await helper.assets.setMetadata(97 alice,98 USDT_ASSET_ID,99 USDT_ASSET_METADATA_NAME,100 USDT_ASSET_METADATA_DESCRIPTION,101 USDT_ASSET_METADATA_DECIMALS,102 );103 await helper.assets.mint(104 alice,105 USDT_ASSET_ID,106 alice.address,107 USDT_ASSET_AMOUNT,108 );109110 // funding parachain sovereing account on Statemint.111 // The sovereign account should be created before any action112 // (the assets pallet on Statemint check if the sovereign account exists)113 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);114 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);115 });116117118 await usingPlaygrounds(async (helper) => {119 const location = {120 V2: {121 parents: 1,122 interior: {X3: [123 {124 Parachain: STATEMINT_CHAIN,125 },126 {127 PalletInstance: STATEMINT_PALLET_INSTANCE,128 },129 {130 GeneralIndex: USDT_ASSET_ID,131 },132 ]},133 },134 };135136 const metadata =137 {138 name: USDT_ASSET_ID,139 symbol: USDT_ASSET_METADATA_NAME,140 decimals: USDT_ASSET_METADATA_DECIMALS,141 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,142 };143 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);144 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);145 });146147148 // Providing the relay currency to the unique sender account149 // (fee for USDT XCM are paid in relay tokens)150 await usingRelayPlaygrounds(relayUrl, async (helper) => {151 const destination = {152 V2: {153 parents: 0,154 interior: {X1: {155 Parachain: UNIQUE_CHAIN,156 },157 },158 }};159160 const beneficiary = {161 V2: {162 parents: 0,163 interior: {X1: {164 AccountId32: {165 network: 'Any',166 id: alice.addressRaw,167 },168 }},169 },170 };171172 const assets = {173 V2: [174 {175 id: {176 Concrete: {177 parents: 0,178 interior: 'Here',179 },180 },181 fun: {182 Fungible: TRANSFER_AMOUNT_RELAY,183 },184 },185 ],186 };187188 const feeAssetItem = 0;189190 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');191 });192193 });194195 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {196 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {197 const dest = {198 V2: {199 parents: 1,200 interior: {X1: {201 Parachain: UNIQUE_CHAIN,202 },203 },204 }};205206 const beneficiary = {207 V2: {208 parents: 0,209 interior: {X1: {210 AccountId32: {211 network: 'Any',212 id: alice.addressRaw,213 },214 }},215 },216 };217218 const assets = {219 V2: [220 {221 id: {222 Concrete: {223 parents: 0,224 interior: {225 X2: [226 {227 PalletInstance: STATEMINT_PALLET_INSTANCE,228 },229 {230 GeneralIndex: USDT_ASSET_ID,231 },232 ]},233 },234 },235 fun: {236 Fungible: TRANSFER_AMOUNT,237 },238 },239 ],240 };241242 const feeAssetItem = 0;243244 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);245 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');246247 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);248249 // common good parachain take commission in it native token250 console.log(251 '[Statemint -> Unique] transaction fees on Statemint: %s WND',252 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),253 );254 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;255256 });257258259 // ensure that asset has been delivered260 await helper.wait.newBlocks(3);261262 // expext collection id will be with id 1263 const free = await helper.ft.getBalance(1, {Substrate: alice.address});264265 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);266267 console.log(268 '[Statemint -> Unique] transaction fees on Unique: %s USDT',269 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),270 );271 console.log(272 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',273 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),274 );275 // commission has not paid in USDT token276 expect(free).to.be.equal(TRANSFER_AMOUNT);277 // ... and parachain native token278 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;279 });280281 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {282 const destination = {283 V2: {284 parents: 1,285 interior: {X2: [286 {287 Parachain: STATEMINT_CHAIN,288 },289 {290 AccountId32: {291 network: 'Any',292 id: alice.addressRaw,293 },294 },295 ]},296 },297 };298299 const relayFee = 400_000_000_000_000n;300 const currencies: [any, bigint][] = [301 [302 {303 ForeignAssetId: 0,304 },305 TRANSFER_AMOUNT,306 ],307 [308 {309 NativeAssetId: 'Parent',310 },311 relayFee,312 ],313 ];314315 const feeItem = 1;316317 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');318319 // the commission has been paid in parachain native token320 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);321 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));322 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;323324 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {325 await helper.wait.newBlocks(3);326327 // The USDT token never paid fees. Its amount not changed from begin value.328 // Also check that xcm transfer has been succeeded329 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;330 });331 });332333 itSub('Should connect and send Relay token to Unique', async ({helper}) => {334 balanceBobBefore = await helper.balance.getSubstrate(bob.address);335 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});336337 await usingRelayPlaygrounds(relayUrl, async (helper) => {338 const destination = {339 V2: {340 parents: 0,341 interior: {X1: {342 Parachain: UNIQUE_CHAIN,343 },344 },345 }};346347 const beneficiary = {348 V2: {349 parents: 0,350 interior: {X1: {351 AccountId32: {352 network: 'Any',353 id: bob.addressRaw,354 },355 }},356 },357 };358359 const assets = {360 V2: [361 {362 id: {363 Concrete: {364 parents: 0,365 interior: 'Here',366 },367 },368 fun: {369 Fungible: TRANSFER_AMOUNT_RELAY,370 },371 },372 ],373 };374375 const feeAssetItem = 0;376377 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');378 });379380 await helper.wait.newBlocks(3);381382 balanceBobAfter = await helper.balance.getSubstrate(bob.address);383 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});384385 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;386 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;387 console.log(388 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',389 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),390 );391 console.log(392 '[Relay (Westend) -> Unique] transaction fees: %s WND',393 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),394 );395 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);396 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;397 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;398 });399400 itSub('Should connect and send Relay token back', async ({helper}) => {401 let relayTokenBalanceBefore: bigint;402 let relayTokenBalanceAfter: bigint;403 await usingRelayPlaygrounds(relayUrl, async (helper) => {404 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);405 });406407 const destination = {408 V2: {409 parents: 1,410 interior: {411 X1:{412 AccountId32: {413 network: 'Any',414 id: bob.addressRaw,415 },416 },417 },418 },419 };420421 const currencies: any = [422 [423 {424 NativeAssetId: 'Parent',425 },426 TRANSFER_AMOUNT_RELAY,427 ],428 ];429430 const feeItem = 0;431432 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');433434 balanceBobFinal = await helper.balance.getSubstrate(bob.address);435 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));436437 await usingRelayPlaygrounds(relayUrl, async (helper) => {438 await helper.wait.newBlocks(10);439 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);440441 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;442 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));443 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;444 });445 });446});447448describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {449 let alice: IKeyringPair;450 let randomAccount: IKeyringPair;451452 let balanceUniqueTokenInit: bigint;453 let balanceUniqueTokenMiddle: bigint;454 let balanceUniqueTokenFinal: bigint;455 let balanceAcalaTokenInit: bigint;456 let balanceAcalaTokenMiddle: bigint;457 let balanceAcalaTokenFinal: bigint;458 let balanceUniqueForeignTokenInit: bigint;459 let balanceUniqueForeignTokenMiddle: bigint;460 let balanceUniqueForeignTokenFinal: bigint;461462 // computed by a test transfer from prod Unique to prod Acala.463 // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48464 // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)465 const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;466 const acalaEps = 8n * 10n ** 16n;467468 let acalaBackwardTransferAmount: bigint;469470 before(async () => {471 await usingPlaygrounds(async (helper, privateKey) => {472 alice = await privateKey('//Alice');473 [randomAccount] = await helper.arrange.createAccounts([0n], alice);474475 // Set the default version to wrap the first message to other chains.476 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);477 });478479 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {480 const destination = {481 V2: {482 parents: 1,483 interior: {484 X1: {485 Parachain: UNIQUE_CHAIN,486 },487 },488 },489 };490491 const metadata = {492 name: 'Unique Network',493 symbol: 'UNQ',494 decimals: 18,495 minimalBalance: 1250000000000000000n,496 };497 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>498 hexToString(v.toJSON()['symbol'])) as string[];499500 if(!assets.includes('UNQ')) {501 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);502 } else {503 console.log('UNQ token already registered on Acala assetRegistry pallet');504 }505 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);506 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);507 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});508 });509510 await usingPlaygrounds(async (helper) => {511 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);512 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);513 });514 });515516 itSub('Should connect and send UNQ to Acala', async ({helper}) => {517518 const destination = {519 V2: {520 parents: 1,521 interior: {522 X1: {523 Parachain: ACALA_CHAIN,524 },525 },526 },527 };528529 const beneficiary = {530 V2: {531 parents: 0,532 interior: {533 X1: {534 AccountId32: {535 network: 'Any',536 id: randomAccount.addressRaw,537 },538 },539 },540 },541 };542543 const assets = {544 V2: [545 {546 id: {547 Concrete: {548 parents: 0,549 interior: 'Here',550 },551 },552 fun: {553 Fungible: TRANSFER_AMOUNT,554 },555 },556 ],557 };558559 const feeAssetItem = 0;560561 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');562 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);563564 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;565 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));566 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;567568 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {569 await helper.wait.newBlocks(3);570571 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});572 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);573574 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;575 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;576 acalaBackwardTransferAmount = unqIncomeTransfer;577578 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;579580 console.log(581 '[Unique -> Acala] transaction fees on Acala: %s ACA',582 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),583 );584 console.log(585 '[Unique -> Acala] transaction fees on Acala: %s UNQ',586 helper.util.bigIntToDecimals(acaUnqFees),587 );588 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));589 expect(acaFees == 0n).to.be.true;590591 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;592593 expect(594 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,595 'Acala took different income fee, check the Acala foreign asset config',596 ).to.be.true;597 });598 });599600 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {601 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {602 const destination = {603 V2: {604 parents: 1,605 interior: {606 X2: [607 {Parachain: UNIQUE_CHAIN},608 {609 AccountId32: {610 network: 'Any',611 id: randomAccount.addressRaw,612 },613 },614 ],615 },616 },617 };618619 const id = {620 ForeignAsset: 0,621 };622623 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');624 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);625 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);626627 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;628 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;629630 console.log(631 '[Acala -> Unique] transaction fees on Acala: %s ACA',632 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),633 );634 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));635636 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;637 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;638 });639640 await helper.wait.newBlocks(3);641642 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);643 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;644 expect(actuallyDelivered > 0).to.be.true;645646 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));647648 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;649 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));650 expect(unqFees == 0n).to.be.true;651 });652653 itSub('Acala can send only up to its balance', async ({helper}) => {654 // set Acala's sovereign account's balance655 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);656 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);657 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);658659 const moreThanAcalaHas = acalaBalance * 2n;660661 let targetAccountBalance = 0n;662 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);663664 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(665 targetAccount.addressRaw,666 {667 Concrete: {668 parents: 0,669 interior: 'Here',670 },671 },672 moreThanAcalaHas,673 );674675 let maliciousXcmProgramSent: any;676 const maxWaitBlocks = 3;677678 // Try to trick Unique679 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {680 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);681682 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);683 });684685 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash686 && event.outcome.isFailedToTransactAsset);687688 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);689 expect(targetAccountBalance).to.be.equal(0n);690691 // But Acala still can send the correct amount692 const validTransferAmount = acalaBalance / 2n;693 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(694 targetAccount.addressRaw,695 {696 Concrete: {697 parents: 0,698 interior: 'Here',699 },700 },701 validTransferAmount,702 );703704 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {705 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);706 });707708 await helper.wait.newBlocks(maxWaitBlocks);709710 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);711 expect(targetAccountBalance).to.be.equal(validTransferAmount);712 });713714 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {715 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);716 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);717718 const uniqueMultilocation = {719 V2: {720 parents: 1,721 interior: {722 X1: {723 Parachain: UNIQUE_CHAIN,724 },725 },726 },727 };728729 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(730 targetAccount.addressRaw,731 {732 Concrete: {733 parents: 1,734 interior: {735 X1: {736 Parachain: UNIQUE_CHAIN,737 },738 },739 },740 },741 testAmount,742 );743744 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(745 targetAccount.addressRaw,746 {747 Concrete: {748 parents: 0,749 interior: 'Here',750 },751 },752 testAmount,753 );754755 let maliciousXcmProgramFullIdSent: any;756 let maliciousXcmProgramHereIdSent: any;757 const maxWaitBlocks = 3;758759 // Try to trick Unique using full UNQ identification760 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {761 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);762763 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);764 });765766 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash767 && event.outcome.isUntrustedReserveLocation);768769 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);770 expect(accountBalance).to.be.equal(0n);771772 // Try to trick Unique using shortened UNQ identification773 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {774 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);775776 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);777 });778779 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash780 && event.outcome.isUntrustedReserveLocation);781782 accountBalance = await helper.balance.getSubstrate(targetAccount.address);783 expect(accountBalance).to.be.equal(0n);784 });785});786787describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {788 let alice: IKeyringPair;789 let randomAccount: IKeyringPair;790 let unqFees: bigint;791 let balanceUniqueTokenInit: bigint;792 let balanceUniqueTokenMiddle: bigint;793 let balanceUniqueTokenFinal: bigint;794 const maxWaitBlocks = 6;795796 before(async () => {797 await usingPlaygrounds(async (helper, privateKey) => {798 alice = await privateKey('//Alice');799 [randomAccount] = await helper.arrange.createAccounts([0n], alice);800801 // Set the default version to wrap the first message to other chains.802 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);803 });804805 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {806 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))807 .toJSON() as [])808 .map(nToBigInt).length != 0;809 /*810 Check whether the Unique token has been added811 to the whitelist, since an error will occur812 if it is added again. Needed for debugging813 when this test is run multiple times.814 */815 if(isWhitelisted) {816 console.log('UNQ token is already whitelisted on Polkadex');817 } else {818 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);819 }820821 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);822 });823824 await usingPlaygrounds(async (helper) => {825 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);826 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);827 });828 });829830 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {831832 const destination = {833 V2: {834 parents: 1,835 interior: {836 X1: {837 Parachain: POLKADEX_CHAIN,838 },839 },840 },841 };842843 const beneficiary = {844 V2: {845 parents: 0,846 interior: {847 X1: {848 AccountId32: {849 network: 'Any',850 id: randomAccount.addressRaw,851 },852 },853 },854 },855 };856857 const assets = {858 V2: [859 {860 id: {861 Concrete: {862 parents: 0,863 interior: 'Here',864 },865 },866 fun: {867 Fungible: TRANSFER_AMOUNT,868 },869 },870 ],871 };872873 const feeAssetItem = 0;874875 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');876 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);877 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);878879 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;880 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));881 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;882883 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {884 /*885 Since only the parachain part of the Polkadex886 infrastructure is launched (without their887 solochain validators), processing incoming888 assets will lead to an error.889 This error indicates that the Polkadex chain890 received a message from the Unique network,891 since the hash is being checked to ensure892 it matches what was sent.893 */894 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);895 });896 });897898899 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {900901 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(902 randomAccount.addressRaw,903 {904 Concrete: {905 parents: 1,906 interior: {907 X1: {Parachain: UNIQUE_CHAIN},908 },909 },910 },911 TRANSFER_AMOUNT,912 );913914 let xcmProgramSent: any;915916917 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {918 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, xcmProgram);919920 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);921 });922923 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);924925 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);926927 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);928 });929930 itSub('Polkadex can send only up to its balance', async ({helper}) => {931 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);932 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);933 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);934 const moreThanPolkadexHas = 2n * polkadexBalance;935936 const targetAccount = helper.arrange.createEmptyAccount();937938 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(939 targetAccount.addressRaw,940 {941 Concrete: {942 parents: 0,943 interior: 'Here',944 },945 },946 moreThanPolkadexHas,947 );948949 let maliciousXcmProgramSent: any;950951952 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {953 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);954955 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);956 });957958 await expectFailedToTransact(helper, maliciousXcmProgramSent);959960 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);961 expect(targetAccountBalance).to.be.equal(0n);962 });963964 itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {965 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);966 const targetAccount = helper.arrange.createEmptyAccount();967968 const uniqueMultilocation = {969 V2: {970 parents: 1,971 interior: {972 X1: {973 Parachain: UNIQUE_CHAIN,974 },975 },976 },977 };978979 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(980 targetAccount.addressRaw,981 {982 Concrete: {983 parents: 1,984 interior: {985 X1: {986 Parachain: UNIQUE_CHAIN,987 },988 },989 },990 },991 testAmount,992 );993994 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(995 targetAccount.addressRaw,996 {997 Concrete: {998 parents: 0,999 interior: 'Here',1000 },1001 },1002 testAmount,1003 );10041005 let maliciousXcmProgramFullIdSent: any;1006 let maliciousXcmProgramHereIdSent: any;1007 const maxWaitBlocks = 3;10081009 // Try to trick Unique using full UNQ identification1010 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1011 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10121013 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1014 });10151016 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10171018 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1019 expect(accountBalance).to.be.equal(0n);10201021 // Try to trick Unique using shortened UNQ identification1022 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1023 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10241025 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1026 });10271028 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10291030 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1031 expect(accountBalance).to.be.equal(0n);1032 });1033});10341035// These tests are relevant only when1036// the the corresponding foreign assets are not registered1037describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1038 let alice: IKeyringPair;1039 let alith: IKeyringPair;10401041 const testAmount = 100_000_000_000n;1042 let uniqueParachainJunction;1043 let uniqueAccountJunction;10441045 let uniqueParachainMultilocation: any;1046 let uniqueAccountMultilocation: any;1047 let uniqueCombinedMultilocation: any;1048 let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V210491050 let messageSent: any;10511052 const maxWaitBlocks = 3;10531054 before(async () => {1055 await usingPlaygrounds(async (helper, privateKey) => {1056 alice = await privateKey('//Alice');10571058 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1059 uniqueAccountJunction = {1060 AccountId32: {1061 network: 'Any',1062 id: alice.addressRaw,1063 },1064 };10651066 uniqueParachainMultilocation = {1067 V2: {1068 parents: 1,1069 interior: {1070 X1: uniqueParachainJunction,1071 },1072 },1073 };10741075 uniqueAccountMultilocation = {1076 V2: {1077 parents: 0,1078 interior: {1079 X1: uniqueAccountJunction,1080 },1081 },1082 };10831084 uniqueCombinedMultilocation = {1085 V2: {1086 parents: 1,1087 interior: {1088 X2: [uniqueParachainJunction, uniqueAccountJunction],1089 },1090 },1091 };10921093 uniqueCombinedMultilocationAcala = {1094 V2: {1095 parents: 1,1096 interior: {1097 X2: [uniqueParachainJunction, uniqueAccountJunction],1098 },1099 },1100 };11011102 // Set the default version to wrap the first message to other chains.1103 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1104 });11051106 // eslint-disable-next-line require-await1107 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1108 alith = helper.account.alithAccount();1109 });1110 });1111111211131114 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1115 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1116 const id = {1117 Token: 'ACA',1118 };1119 const destination = uniqueCombinedMultilocationAcala;1120 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11211122 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1123 });11241125 await expectFailedToTransact(helper, messageSent);1126 });11271128 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1129 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1130 const id = 'SelfReserve';1131 const destination = uniqueCombinedMultilocation;1132 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11331134 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1135 });11361137 await expectFailedToTransact(helper, messageSent);1138 });11391140 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1141 await usingAstarPlaygrounds(astarUrl, async (helper) => {1142 const destinationParachain = uniqueParachainMultilocation;1143 const beneficiary = uniqueAccountMultilocation;1144 const assets = {1145 V2: [{1146 id: {1147 Concrete: {1148 parents: 0,1149 interior: 'Here',1150 },1151 },1152 fun: {1153 Fungible: testAmount,1154 },1155 }],1156 };1157 const feeAssetItem = 0;11581159 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1160 destinationParachain,1161 beneficiary,1162 assets,1163 feeAssetItem,1164 ]);11651166 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1167 });11681169 await expectFailedToTransact(helper, messageSent);1170 });11711172 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {11731174 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1175 helper.arrange.createEmptyAccount().addressRaw,1176 {1177 Concrete: {1178 parents: 1,1179 interior: {1180 X1: {1181 Parachain: POLKADEX_CHAIN,1182 },1183 },1184 },1185 },1186 testAmount,1187 );11881189 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1190 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1191 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1192 });11931194 await expectFailedToTransact(helper, messageSent);1195 });1196});11971198describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1199 // Unique constants1200 let alice: IKeyringPair;1201 let uniqueAssetLocation;12021203 let randomAccountUnique: IKeyringPair;1204 let randomAccountMoonbeam: IKeyringPair;12051206 // Moonbeam constants1207 let assetId: string;12081209 const uniqueAssetMetadata = {1210 name: 'xcUnique',1211 symbol: 'xcUNQ',1212 decimals: 18,1213 isFrozen: false,1214 minimalBalance: 1n,1215 };12161217 let balanceUniqueTokenInit: bigint;1218 let balanceUniqueTokenMiddle: bigint;1219 let balanceUniqueTokenFinal: bigint;1220 let balanceForeignUnqTokenInit: bigint;1221 let balanceForeignUnqTokenMiddle: bigint;1222 let balanceForeignUnqTokenFinal: bigint;1223 let balanceGlmrTokenInit: bigint;1224 let balanceGlmrTokenMiddle: bigint;1225 let balanceGlmrTokenFinal: bigint;12261227 before(async () => {1228 await usingPlaygrounds(async (helper, privateKey) => {1229 alice = await privateKey('//Alice');1230 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12311232 balanceForeignUnqTokenInit = 0n;12331234 // Set the default version to wrap the first message to other chains.1235 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1236 });12371238 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1239 const alithAccount = helper.account.alithAccount();1240 const baltatharAccount = helper.account.baltatharAccount();1241 const dorothyAccount = helper.account.dorothyAccount();12421243 randomAccountMoonbeam = helper.account.create();12441245 // >>> Sponsoring Dorothy >>>1246 console.log('Sponsoring Dorothy.......');1247 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1248 console.log('Sponsoring Dorothy.......DONE');1249 // <<< Sponsoring Dorothy <<<12501251 uniqueAssetLocation = {1252 XCM: {1253 parents: 1,1254 interior: {X1: {Parachain: UNIQUE_CHAIN}},1255 },1256 };1257 const existentialDeposit = 1n;1258 const isSufficient = true;1259 const unitsPerSecond = 1n;1260 const numAssetsWeightHint = 0;12611262 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {1263 console.log('Unique asset is already registered on MoonBeam');1264 } else {1265 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1266 location: uniqueAssetLocation,1267 metadata: uniqueAssetMetadata,1268 existentialDeposit,1269 isSufficient,1270 unitsPerSecond,1271 numAssetsWeightHint,1272 });12731274 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);12751276 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);1277 }12781279 // >>> Acquire Unique AssetId Info on Moonbeam >>>1280 console.log('Acquire Unique AssetId Info on Moonbeam.......');12811282 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();1283 console.log('UNQ asset ID is %s', assetId);1284 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1285 // >>> Acquire Unique AssetId Info on Moonbeam >>>12861287 // >>> Sponsoring random Account >>>1288 console.log('Sponsoring random Account.......');1289 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1290 console.log('Sponsoring random Account.......DONE');1291 // <<< Sponsoring random Account <<<12921293 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1294 });12951296 await usingPlaygrounds(async (helper) => {1297 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1298 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1299 });1300 });13011302 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1303 const currencyId = {1304 NativeAssetId: 'Here',1305 };1306 const dest = {1307 V2: {1308 parents: 1,1309 interior: {1310 X2: [1311 {Parachain: MOONBEAM_CHAIN},1312 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1313 ],1314 },1315 },1316 };1317 const amount = TRANSFER_AMOUNT;13181319 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13201321 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1322 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13231324 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1325 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1326 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13271328 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1329 await helper.wait.newBlocks(3);1330 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13311332 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1333 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1334 expect(glmrFees == 0n).to.be.true;13351336 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13371338 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1339 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1340 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1341 });1342 });13431344 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1345 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1346 const asset = {1347 V2: {1348 id: {1349 Concrete: {1350 parents: 1,1351 interior: {1352 X1: {Parachain: UNIQUE_CHAIN},1353 },1354 },1355 },1356 fun: {1357 Fungible: TRANSFER_AMOUNT,1358 },1359 },1360 };1361 const destination = {1362 V2: {1363 parents: 1,1364 interior: {1365 X2: [1366 {Parachain: UNIQUE_CHAIN},1367 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1368 ],1369 },1370 },1371 };13721373 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');13741375 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);13761377 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1378 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1379 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;13801381 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);13821383 expect(unqRandomAccountAsset).to.be.null;13841385 balanceForeignUnqTokenFinal = 0n;13861387 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1388 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1389 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1390 });13911392 await helper.wait.newBlocks(3);13931394 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1395 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1396 expect(actuallyDelivered > 0).to.be.true;13971398 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));13991400 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1401 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1402 expect(unqFees == 0n).to.be.true;1403 });14041405 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1406 // set Moonbeam's sovereign account's balance1407 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1408 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1409 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14101411 const moreThanMoonbeamHas = moonbeamBalance * 2n;14121413 let targetAccountBalance = 0n;1414 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14151416 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1417 targetAccount.addressRaw,1418 {1419 Concrete: {1420 parents: 0,1421 interior: 'Here',1422 },1423 },1424 moreThanMoonbeamHas,1425 );14261427 let maliciousXcmProgramSent: any;1428 const maxWaitBlocks = 3;14291430 // Try to trick Unique1431 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1432 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);14331434 // Needed to bypass the call filter.1435 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1436 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14371438 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1439 });14401441 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1442 && event.outcome.isFailedToTransactAsset);14431444 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1445 expect(targetAccountBalance).to.be.equal(0n);14461447 // But Moonbeam still can send the correct amount1448 const validTransferAmount = moonbeamBalance / 2n;1449 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1450 targetAccount.addressRaw,1451 {1452 Concrete: {1453 parents: 0,1454 interior: 'Here',1455 },1456 },1457 validTransferAmount,1458 );14591460 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1461 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, validXcmProgram]);14621463 // Needed to bypass the call filter.1464 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1465 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1466 });14671468 await helper.wait.newBlocks(maxWaitBlocks);14691470 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1471 expect(targetAccountBalance).to.be.equal(validTransferAmount);1472 });14731474 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1475 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1476 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);14771478 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1479 targetAccount.addressRaw,1480 {1481 Concrete: {1482 parents: 0,1483 interior: {1484 X1: {1485 Parachain: UNIQUE_CHAIN,1486 },1487 },1488 },1489 },1490 testAmount,1491 );14921493 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1494 targetAccount.addressRaw,1495 {1496 Concrete: {1497 parents: 0,1498 interior: 'Here',1499 },1500 },1501 testAmount,1502 );15031504 let maliciousXcmProgramFullIdSent: any;1505 let maliciousXcmProgramHereIdSent: any;1506 const maxWaitBlocks = 3;15071508 // Try to trick Unique using full UNQ identification1509 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1510 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);15111512 // Needed to bypass the call filter.1513 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1514 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15151516 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1517 });15181519 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1520 && event.outcome.isUntrustedReserveLocation);15211522 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1523 expect(accountBalance).to.be.equal(0n);15241525 // Try to trick Unique using shortened UNQ identification1526 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1527 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);15281529 // Needed to bypass the call filter.1530 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1531 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15321533 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1534 });15351536 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1537 && event.outcome.isUntrustedReserveLocation);15381539 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1540 expect(accountBalance).to.be.equal(0n);1541 });1542});15431544describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1545 let alice: IKeyringPair;1546 let randomAccount: IKeyringPair;15471548 const UNQ_ASSET_ID_ON_ASTAR = 1;1549 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;15501551 // Unique -> Astar1552 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1553 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1554 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1555 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens15561557 // Astar -> Unique1558 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1559 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ15601561 let balanceAfterUniqueToAstarXCM: bigint;15621563 before(async () => {1564 await usingPlaygrounds(async (helper, privateKey) => {1565 alice = await privateKey('//Alice');1566 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1567 console.log('randomAccount', randomAccount.address);15681569 // Set the default version to wrap the first message to other chains.1570 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1571 });15721573 await usingAstarPlaygrounds(astarUrl, async (helper) => {1574 if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {1575 console.log('1. Create foreign asset and metadata');1576 // TODO update metadata with values from production1577 await helper.assets.create(1578 alice,1579 UNQ_ASSET_ID_ON_ASTAR,1580 alice.address,1581 UNQ_MINIMAL_BALANCE_ON_ASTAR,1582 );15831584 await helper.assets.setMetadata(1585 alice,1586 UNQ_ASSET_ID_ON_ASTAR,1587 'Cross chain UNQ',1588 'xcUNQ',1589 Number(UNQ_DECIMALS),1590 );15911592 console.log('2. Register asset location on Astar');1593 const assetLocation = {1594 V2: {1595 parents: 1,1596 interior: {1597 X1: {1598 Parachain: UNIQUE_CHAIN,1599 },1600 },1601 },1602 };16031604 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16051606 console.log('3. Set UNQ payment for XCM execution on Astar');1607 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);1608 } else {1609 console.log('UNQ is already registered on Astar');1610 }1611 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1612 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1613 });1614 });16151616 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1617 const destination = {1618 V2: {1619 parents: 1,1620 interior: {1621 X1: {1622 Parachain: ASTAR_CHAIN,1623 },1624 },1625 },1626 };16271628 const beneficiary = {1629 V2: {1630 parents: 0,1631 interior: {1632 X1: {1633 AccountId32: {1634 network: 'Any',1635 id: randomAccount.addressRaw,1636 },1637 },1638 },1639 },1640 };16411642 const assets = {1643 V2: [1644 {1645 id: {1646 Concrete: {1647 parents: 0,1648 interior: 'Here',1649 },1650 },1651 fun: {1652 Fungible: unqToAstarTransferred,1653 },1654 },1655 ],1656 };16571658 // Initial balance is 100 UNQ1659 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1660 console.log(`Initial balance is: ${balanceBefore}`);16611662 const feeAssetItem = 0;1663 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');16641665 // Balance after reserve transfer is less than 901666 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1667 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1668 console.log(`Unique's UNQ commission is: ${balanceBefore-balanceAfterUniqueToAstarXCM}`);1669 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;16701671 await usingAstarPlaygrounds(astarUrl, async (helper) => {1672 await helper.wait.newBlocks(3);1673 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1674 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);16751676 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1677 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred-xcUNQbalance!}`);16781679 expect(xcUNQbalance).to.eq(unqToAstarArrived);1680 // Astar balance does not changed1681 expect(astarBalance).to.eq(astarInitialBalance);1682 });1683 });16841685 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1686 await usingAstarPlaygrounds(astarUrl, async (helper) => {1687 const destination = {1688 V2: {1689 parents: 1,1690 interior: {1691 X1: {1692 Parachain: UNIQUE_CHAIN,1693 },1694 },1695 },1696 };16971698 const beneficiary = {1699 V2: {1700 parents: 0,1701 interior: {1702 X1: {1703 AccountId32: {1704 network: 'Any',1705 id: randomAccount.addressRaw,1706 },1707 },1708 },1709 },1710 };17111712 const assets = {1713 V2: [1714 {1715 id: {1716 Concrete: {1717 parents: 1,1718 interior: {1719 X1: {1720 Parachain: UNIQUE_CHAIN,1721 },1722 },1723 },1724 },1725 fun: {1726 Fungible: unqFromAstarTransfered,1727 },1728 },1729 ],1730 };17311732 // Initial balance is 1 ASTR1733 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1734 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1735 expect(balanceASTRbefore).to.eq(astarInitialBalance);17361737 const feeAssetItem = 0;1738 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1739 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17401741 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1742 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1743 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17441745 // Assert: xcUNQ balance correctly decreased1746 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1747 // Assert: ASTR balance is 0.996...1748 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1749 });17501751 await helper.wait.newBlocks(3);1752 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1753 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1754 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1755 });17561757 itSub('Astar can send only up to its balance', async ({helper}) => {1758 // set Astar's sovereign account's balance1759 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1760 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1761 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);17621763 const moreThanAstarHas = astarBalance * 2n;17641765 let targetAccountBalance = 0n;1766 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);17671768 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1769 targetAccount.addressRaw,1770 {1771 Concrete: {1772 parents: 0,1773 interior: 'Here',1774 },1775 },1776 moreThanAstarHas,1777 );17781779 let maliciousXcmProgramSent: any;1780 const maxWaitBlocks = 3;17811782 // Try to trick Unique1783 await usingAstarPlaygrounds(astarUrl, async (helper) => {1784 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);17851786 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1787 });17881789 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1790 && event.outcome.isFailedToTransactAsset);17911792 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1793 expect(targetAccountBalance).to.be.equal(0n);17941795 // But Astar still can send the correct amount1796 const validTransferAmount = astarBalance / 2n;1797 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1798 targetAccount.addressRaw,1799 {1800 Concrete: {1801 parents: 0,1802 interior: 'Here',1803 },1804 },1805 validTransferAmount,1806 );18071808 await usingAstarPlaygrounds(astarUrl, async (helper) => {1809 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);1810 });18111812 await helper.wait.newBlocks(maxWaitBlocks);18131814 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1815 expect(targetAccountBalance).to.be.equal(validTransferAmount);1816 });18171818 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1819 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1820 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18211822 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1823 targetAccount.addressRaw,1824 {1825 Concrete: {1826 parents: 1,1827 interior: {1828 X1: {1829 Parachain: UNIQUE_CHAIN,1830 },1831 },1832 },1833 },1834 testAmount,1835 );18361837 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1838 targetAccount.addressRaw,1839 {1840 Concrete: {1841 parents: 0,1842 interior: 'Here',1843 },1844 },1845 testAmount,1846 );18471848 let maliciousXcmProgramFullIdSent: any;1849 let maliciousXcmProgramHereIdSent: any;1850 const maxWaitBlocks = 3;18511852 // Try to trick Unique using full UNQ identification1853 await usingAstarPlaygrounds(astarUrl, async (helper) => {1854 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramFullId);18551856 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1857 });18581859 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1860 && event.outcome.isUntrustedReserveLocation);18611862 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1863 expect(accountBalance).to.be.equal(0n);18641865 // Try to trick Unique using shortened UNQ identification1866 await usingAstarPlaygrounds(astarUrl, async (helper) => {1867 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramHereId);18681869 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1870 });18711872 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1873 && event.outcome.isUntrustedReserveLocation);18741875 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1876 expect(accountBalance).to.be.equal(0n);1877 });1878});