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});1// 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 uniqueAssetId,732 testAmount,733 );734735 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(736 targetAccount.addressRaw,737 {738 Concrete: {739 parents: 0,740 interior: 'Here',741 },742 },743 testAmount,744 );745746 let maliciousXcmProgramFullIdSent: any;747 let maliciousXcmProgramHereIdSent: any;748 const maxWaitBlocks = 3;749750 // Try to trick Unique using full UNQ identification751 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {752 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);753754 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);755 });756757 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash758 && event.outcome.isUntrustedReserveLocation);759760 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);761 expect(accountBalance).to.be.equal(0n);762763 // Try to trick Unique using shortened UNQ identification764 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {765 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);766767 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);768 });769770 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash771 && event.outcome.isUntrustedReserveLocation);772773 accountBalance = await helper.balance.getSubstrate(targetAccount.address);774 expect(accountBalance).to.be.equal(0n);775 });776});777778describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {779 let alice: IKeyringPair;780 let randomAccount: IKeyringPair;781 let unqFees: bigint;782 let balanceUniqueTokenInit: bigint;783 let balanceUniqueTokenMiddle: bigint;784 let balanceUniqueTokenFinal: bigint;785 const maxWaitBlocks = 6;786787 before(async () => {788 await usingPlaygrounds(async (helper, privateKey) => {789 alice = await privateKey('//Alice');790 [randomAccount] = await helper.arrange.createAccounts([0n], alice);791792 // Set the default version to wrap the first message to other chains.793 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);794 });795796 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {797 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))798 .toJSON() as [])799 .map(nToBigInt).length != 0;800 /*801 Check whether the Unique token has been added802 to the whitelist, since an error will occur803 if it is added again. Needed for debugging804 when this test is run multiple times.805 */806 if(isWhitelisted) {807 console.log('UNQ token is already whitelisted on Polkadex');808 } else {809 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);810 }811812 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);813 });814815 await usingPlaygrounds(async (helper) => {816 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);817 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);818 });819 });820821 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {822823 const destination = {824 V2: {825 parents: 1,826 interior: {827 X1: {828 Parachain: POLKADEX_CHAIN,829 },830 },831 },832 };833834 const beneficiary = {835 V2: {836 parents: 0,837 interior: {838 X1: {839 AccountId32: {840 network: 'Any',841 id: randomAccount.addressRaw,842 },843 },844 },845 },846 };847848 const assets = {849 V2: [850 {851 id: {852 Concrete: {853 parents: 0,854 interior: 'Here',855 },856 },857 fun: {858 Fungible: TRANSFER_AMOUNT,859 },860 },861 ],862 };863864 const feeAssetItem = 0;865866 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');867 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);868 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);869870 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;871 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));872 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;873874 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {875 /*876 Since only the parachain part of the Polkadex877 infrastructure is launched (without their878 solochain validators), processing incoming879 assets will lead to an error.880 This error indicates that the Polkadex chain881 received a message from the Unique network,882 since the hash is being checked to ensure883 it matches what was sent.884 */885 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);886 });887 });888889890 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {891892 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(893 randomAccount.addressRaw,894 uniqueAssetId,895 TRANSFER_AMOUNT,896 );897898 let xcmProgramSent: any;899900901 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {902 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, xcmProgram);903904 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);905 });906907 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);908909 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);910911 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);912 });913914 itSub('Polkadex can send only up to its balance', async ({helper}) => {915 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);916 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);917 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);918 const moreThanPolkadexHas = 2n * polkadexBalance;919920 const targetAccount = helper.arrange.createEmptyAccount();921922 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(923 targetAccount.addressRaw,924 {925 Concrete: {926 parents: 0,927 interior: 'Here',928 },929 },930 moreThanPolkadexHas,931 );932933 let maliciousXcmProgramSent: any;934935936 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {937 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);938939 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);940 });941942 await expectFailedToTransact(helper, maliciousXcmProgramSent);943944 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);945 expect(targetAccountBalance).to.be.equal(0n);946 });947948 itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {949 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);950 const targetAccount = helper.arrange.createEmptyAccount();951952 const uniqueMultilocation = {953 V2: {954 parents: 1,955 interior: {956 X1: {957 Parachain: UNIQUE_CHAIN,958 },959 },960 },961 };962963 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(964 targetAccount.addressRaw,965 uniqueAssetId,966 testAmount,967 );968969 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(970 targetAccount.addressRaw,971 {972 Concrete: {973 parents: 0,974 interior: 'Here',975 },976 },977 testAmount,978 );979980 let maliciousXcmProgramFullIdSent: any;981 let maliciousXcmProgramHereIdSent: any;982 const maxWaitBlocks = 3;983984 // Try to trick Unique using full UNQ identification985 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {986 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);987988 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);989 });990991 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);992993 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);994 expect(accountBalance).to.be.equal(0n);995996 // Try to trick Unique using shortened UNQ identification997 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {998 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);9991000 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1001 });10021003 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10041005 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1006 expect(accountBalance).to.be.equal(0n);1007 });1008});10091010// These tests are relevant only when1011// the the corresponding foreign assets are not registered1012describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1013 let alice: IKeyringPair;1014 let alith: IKeyringPair;10151016 const testAmount = 100_000_000_000n;1017 let uniqueParachainJunction;1018 let uniqueAccountJunction;10191020 let uniqueParachainMultilocation: any;1021 let uniqueAccountMultilocation: any;1022 let uniqueCombinedMultilocation: any;1023 let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V210241025 let messageSent: any;10261027 const maxWaitBlocks = 3;10281029 before(async () => {1030 await usingPlaygrounds(async (helper, privateKey) => {1031 alice = await privateKey('//Alice');10321033 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1034 uniqueAccountJunction = {1035 AccountId32: {1036 network: 'Any',1037 id: alice.addressRaw,1038 },1039 };10401041 uniqueParachainMultilocation = {1042 V2: {1043 parents: 1,1044 interior: {1045 X1: uniqueParachainJunction,1046 },1047 },1048 };10491050 uniqueAccountMultilocation = {1051 V2: {1052 parents: 0,1053 interior: {1054 X1: uniqueAccountJunction,1055 },1056 },1057 };10581059 uniqueCombinedMultilocation = {1060 V2: {1061 parents: 1,1062 interior: {1063 X2: [uniqueParachainJunction, uniqueAccountJunction],1064 },1065 },1066 };10671068 uniqueCombinedMultilocationAcala = {1069 V2: {1070 parents: 1,1071 interior: {1072 X2: [uniqueParachainJunction, uniqueAccountJunction],1073 },1074 },1075 };10761077 // Set the default version to wrap the first message to other chains.1078 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1079 });10801081 // eslint-disable-next-line require-await1082 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1083 alith = helper.account.alithAccount();1084 });1085 });1086108710881089 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1090 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1091 const id = {1092 Token: 'ACA',1093 };1094 const destination = uniqueCombinedMultilocationAcala;1095 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');10961097 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1098 });10991100 await expectFailedToTransact(helper, messageSent);1101 });11021103 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1104 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1105 const id = 'SelfReserve';1106 const destination = uniqueCombinedMultilocation;1107 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11081109 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1110 });11111112 await expectFailedToTransact(helper, messageSent);1113 });11141115 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1116 await usingAstarPlaygrounds(astarUrl, async (helper) => {1117 const destinationParachain = uniqueParachainMultilocation;1118 const beneficiary = uniqueAccountMultilocation;1119 const assets = {1120 V2: [{1121 id: {1122 Concrete: {1123 parents: 0,1124 interior: 'Here',1125 },1126 },1127 fun: {1128 Fungible: testAmount,1129 },1130 }],1131 };1132 const feeAssetItem = 0;11331134 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1135 destinationParachain,1136 beneficiary,1137 assets,1138 feeAssetItem,1139 ]);11401141 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1142 });11431144 await expectFailedToTransact(helper, messageSent);1145 });11461147 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {11481149 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1150 helper.arrange.createEmptyAccount().addressRaw,1151 {1152 Concrete: {1153 parents: 1,1154 interior: {1155 X1: {1156 Parachain: POLKADEX_CHAIN,1157 },1158 },1159 },1160 },1161 testAmount,1162 );11631164 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1165 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1166 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1167 });11681169 await expectFailedToTransact(helper, messageSent);1170 });1171});11721173describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1174 // Unique constants1175 let alice: IKeyringPair;1176 let uniqueAssetLocation;11771178 let randomAccountUnique: IKeyringPair;1179 let randomAccountMoonbeam: IKeyringPair;11801181 // Moonbeam constants1182 let assetId: string;11831184 const uniqueAssetMetadata = {1185 name: 'xcUnique',1186 symbol: 'xcUNQ',1187 decimals: 18,1188 isFrozen: false,1189 minimalBalance: 1n,1190 };11911192 let balanceUniqueTokenInit: bigint;1193 let balanceUniqueTokenMiddle: bigint;1194 let balanceUniqueTokenFinal: bigint;1195 let balanceForeignUnqTokenInit: bigint;1196 let balanceForeignUnqTokenMiddle: bigint;1197 let balanceForeignUnqTokenFinal: bigint;1198 let balanceGlmrTokenInit: bigint;1199 let balanceGlmrTokenMiddle: bigint;1200 let balanceGlmrTokenFinal: bigint;12011202 before(async () => {1203 await usingPlaygrounds(async (helper, privateKey) => {1204 alice = await privateKey('//Alice');1205 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12061207 balanceForeignUnqTokenInit = 0n;12081209 // Set the default version to wrap the first message to other chains.1210 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1211 });12121213 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1214 const alithAccount = helper.account.alithAccount();1215 const baltatharAccount = helper.account.baltatharAccount();1216 const dorothyAccount = helper.account.dorothyAccount();12171218 randomAccountMoonbeam = helper.account.create();12191220 // >>> Sponsoring Dorothy >>>1221 console.log('Sponsoring Dorothy.......');1222 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1223 console.log('Sponsoring Dorothy.......DONE');1224 // <<< Sponsoring Dorothy <<<12251226 uniqueAssetLocation = {1227 XCM: {1228 parents: 1,1229 interior: {X1: {Parachain: UNIQUE_CHAIN}},1230 },1231 };1232 const existentialDeposit = 1n;1233 const isSufficient = true;1234 const unitsPerSecond = 1n;1235 const numAssetsWeightHint = 0;12361237 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {1238 console.log('Unique asset is already registered on MoonBeam');1239 } else {1240 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1241 location: uniqueAssetLocation,1242 metadata: uniqueAssetMetadata,1243 existentialDeposit,1244 isSufficient,1245 unitsPerSecond,1246 numAssetsWeightHint,1247 });12481249 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);12501251 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);1252 }12531254 // >>> Acquire Unique AssetId Info on Moonbeam >>>1255 console.log('Acquire Unique AssetId Info on Moonbeam.......');12561257 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();1258 console.log('UNQ asset ID is %s', assetId);1259 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1260 // >>> Acquire Unique AssetId Info on Moonbeam >>>12611262 // >>> Sponsoring random Account >>>1263 console.log('Sponsoring random Account.......');1264 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1265 console.log('Sponsoring random Account.......DONE');1266 // <<< Sponsoring random Account <<<12671268 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1269 });12701271 await usingPlaygrounds(async (helper) => {1272 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1273 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1274 });1275 });12761277 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1278 const currencyId = {1279 NativeAssetId: 'Here',1280 };1281 const dest = {1282 V2: {1283 parents: 1,1284 interior: {1285 X2: [1286 {Parachain: MOONBEAM_CHAIN},1287 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1288 ],1289 },1290 },1291 };1292 const amount = TRANSFER_AMOUNT;12931294 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');12951296 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1297 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;12981299 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1300 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1301 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13021303 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1304 await helper.wait.newBlocks(3);1305 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13061307 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1308 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1309 expect(glmrFees == 0n).to.be.true;13101311 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13121313 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1314 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1315 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1316 });1317 });13181319 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1320 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1321 const asset = {1322 V2: {1323 id: {1324 Concrete: {1325 parents: 1,1326 interior: {1327 X1: {Parachain: UNIQUE_CHAIN},1328 },1329 },1330 },1331 fun: {1332 Fungible: TRANSFER_AMOUNT,1333 },1334 },1335 };1336 const destination = {1337 V2: {1338 parents: 1,1339 interior: {1340 X2: [1341 {Parachain: UNIQUE_CHAIN},1342 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1343 ],1344 },1345 },1346 };13471348 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');13491350 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);13511352 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1353 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1354 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;13551356 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);13571358 expect(unqRandomAccountAsset).to.be.null;13591360 balanceForeignUnqTokenFinal = 0n;13611362 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1363 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1364 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1365 });13661367 await helper.wait.newBlocks(3);13681369 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1370 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1371 expect(actuallyDelivered > 0).to.be.true;13721373 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));13741375 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1376 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1377 expect(unqFees == 0n).to.be.true;1378 });13791380 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1381 // set Moonbeam's sovereign account's balance1382 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1383 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1384 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);13851386 const moreThanMoonbeamHas = moonbeamBalance * 2n;13871388 let targetAccountBalance = 0n;1389 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);13901391 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1392 targetAccount.addressRaw,1393 {1394 Concrete: {1395 parents: 0,1396 interior: 'Here',1397 },1398 },1399 moreThanMoonbeamHas,1400 );14011402 let maliciousXcmProgramSent: any;1403 const maxWaitBlocks = 3;14041405 // Try to trick Unique1406 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1407 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);14081409 // Needed to bypass the call filter.1410 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1411 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14121413 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1414 });14151416 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1417 && event.outcome.isFailedToTransactAsset);14181419 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1420 expect(targetAccountBalance).to.be.equal(0n);14211422 // But Moonbeam still can send the correct amount1423 const validTransferAmount = moonbeamBalance / 2n;1424 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1425 targetAccount.addressRaw,1426 {1427 Concrete: {1428 parents: 0,1429 interior: 'Here',1430 },1431 },1432 validTransferAmount,1433 );14341435 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1436 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, validXcmProgram]);14371438 // Needed to bypass the call filter.1439 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1440 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1441 });14421443 await helper.wait.newBlocks(maxWaitBlocks);14441445 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1446 expect(targetAccountBalance).to.be.equal(validTransferAmount);1447 });14481449 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1450 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1451 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);14521453 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1454 targetAccount.addressRaw,1455 uniqueAssetId,1456 testAmount,1457 );14581459 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1460 targetAccount.addressRaw,1461 {1462 Concrete: {1463 parents: 0,1464 interior: 'Here',1465 },1466 },1467 testAmount,1468 );14691470 let maliciousXcmProgramFullIdSent: any;1471 let maliciousXcmProgramHereIdSent: any;1472 const maxWaitBlocks = 3;14731474 // Try to trick Unique using full UNQ identification1475 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1476 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);14771478 // Needed to bypass the call filter.1479 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1480 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);14811482 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1483 });14841485 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1486 && event.outcome.isUntrustedReserveLocation);14871488 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1489 expect(accountBalance).to.be.equal(0n);14901491 // Try to trick Unique using shortened UNQ identification1492 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1493 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);14941495 // Needed to bypass the call filter.1496 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1497 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);14981499 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1500 });15011502 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1503 && event.outcome.isUntrustedReserveLocation);15041505 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1506 expect(accountBalance).to.be.equal(0n);1507 });1508});15091510describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1511 let alice: IKeyringPair;1512 let randomAccount: IKeyringPair;15131514 const UNQ_ASSET_ID_ON_ASTAR = 1;1515 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;15161517 // Unique -> Astar1518 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.1519 const unitsPerSecond = 228_000_000_000n; // This is Phala's value. What will be ours?1520 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ1521 const unqToAstarArrived = 9_999_999_999_088_000_000n; // 9.999 ... UNQ, Astar takes a commision in foreign tokens15221523 // Astar -> Unique1524 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ1525 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.999_999_999_088_000_000n UNQ15261527 let balanceAfterUniqueToAstarXCM: bigint;15281529 before(async () => {1530 await usingPlaygrounds(async (helper, privateKey) => {1531 alice = await privateKey('//Alice');1532 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1533 console.log('randomAccount', randomAccount.address);15341535 // Set the default version to wrap the first message to other chains.1536 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1537 });15381539 await usingAstarPlaygrounds(astarUrl, async (helper) => {1540 if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {1541 console.log('1. Create foreign asset and metadata');1542 // TODO update metadata with values from production1543 await helper.assets.create(1544 alice,1545 UNQ_ASSET_ID_ON_ASTAR,1546 alice.address,1547 UNQ_MINIMAL_BALANCE_ON_ASTAR,1548 );15491550 await helper.assets.setMetadata(1551 alice,1552 UNQ_ASSET_ID_ON_ASTAR,1553 'Cross chain UNQ',1554 'xcUNQ',1555 Number(UNQ_DECIMALS),1556 );15571558 console.log('2. Register asset location on Astar');1559 const assetLocation = {1560 V2: {1561 parents: 1,1562 interior: {1563 X1: {1564 Parachain: UNIQUE_CHAIN,1565 },1566 },1567 },1568 };15691570 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);15711572 console.log('3. Set UNQ payment for XCM execution on Astar');1573 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);1574 } else {1575 console.log('UNQ is already registered on Astar');1576 }1577 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1578 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1579 });1580 });15811582 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1583 const destination = {1584 V2: {1585 parents: 1,1586 interior: {1587 X1: {1588 Parachain: ASTAR_CHAIN,1589 },1590 },1591 },1592 };15931594 const beneficiary = {1595 V2: {1596 parents: 0,1597 interior: {1598 X1: {1599 AccountId32: {1600 network: 'Any',1601 id: randomAccount.addressRaw,1602 },1603 },1604 },1605 },1606 };16071608 const assets = {1609 V2: [1610 {1611 id: {1612 Concrete: {1613 parents: 0,1614 interior: 'Here',1615 },1616 },1617 fun: {1618 Fungible: unqToAstarTransferred,1619 },1620 },1621 ],1622 };16231624 // Initial balance is 100 UNQ1625 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1626 console.log(`Initial balance is: ${balanceBefore}`);16271628 const feeAssetItem = 0;1629 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');16301631 // Balance after reserve transfer is less than 901632 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1633 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1634 console.log(`Unique's UNQ commission is: ${balanceBefore-balanceAfterUniqueToAstarXCM}`);1635 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;16361637 await usingAstarPlaygrounds(astarUrl, async (helper) => {1638 await helper.wait.newBlocks(3);1639 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1640 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);16411642 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1643 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred-xcUNQbalance!}`);16441645 expect(xcUNQbalance).to.eq(unqToAstarArrived);1646 // Astar balance does not changed1647 expect(astarBalance).to.eq(astarInitialBalance);1648 });1649 });16501651 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1652 await usingAstarPlaygrounds(astarUrl, async (helper) => {1653 const destination = {1654 V2: {1655 parents: 1,1656 interior: {1657 X1: {1658 Parachain: UNIQUE_CHAIN,1659 },1660 },1661 },1662 };16631664 const beneficiary = {1665 V2: {1666 parents: 0,1667 interior: {1668 X1: {1669 AccountId32: {1670 network: 'Any',1671 id: randomAccount.addressRaw,1672 },1673 },1674 },1675 },1676 };16771678 const assets = {1679 V2: [1680 {1681 id: {1682 Concrete: {1683 parents: 1,1684 interior: {1685 X1: {1686 Parachain: UNIQUE_CHAIN,1687 },1688 },1689 },1690 },1691 fun: {1692 Fungible: unqFromAstarTransfered,1693 },1694 },1695 ],1696 };16971698 // Initial balance is 1 ASTR1699 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1700 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1701 expect(balanceASTRbefore).to.eq(astarInitialBalance);17021703 const feeAssetItem = 0;1704 // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw1705 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17061707 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1708 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1709 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17101711 // Assert: xcUNQ balance correctly decreased1712 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1713 // Assert: ASTR balance is 0.996...1714 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1715 });17161717 await helper.wait.newBlocks(3);1718 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1719 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1720 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1721 });17221723 itSub('Astar can send only up to its balance', async ({helper}) => {1724 // set Astar's sovereign account's balance1725 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1726 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1727 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);17281729 const moreThanAstarHas = astarBalance * 2n;17301731 let targetAccountBalance = 0n;1732 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);17331734 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1735 targetAccount.addressRaw,1736 {1737 Concrete: {1738 parents: 0,1739 interior: 'Here',1740 },1741 },1742 moreThanAstarHas,1743 );17441745 let maliciousXcmProgramSent: any;1746 const maxWaitBlocks = 3;17471748 // Try to trick Unique1749 await usingAstarPlaygrounds(astarUrl, async (helper) => {1750 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);17511752 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1753 });17541755 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1756 && event.outcome.isFailedToTransactAsset);17571758 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1759 expect(targetAccountBalance).to.be.equal(0n);17601761 // But Astar still can send the correct amount1762 const validTransferAmount = astarBalance / 2n;1763 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1764 targetAccount.addressRaw,1765 {1766 Concrete: {1767 parents: 0,1768 interior: 'Here',1769 },1770 },1771 validTransferAmount,1772 );17731774 await usingAstarPlaygrounds(astarUrl, async (helper) => {1775 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);1776 });17771778 await helper.wait.newBlocks(maxWaitBlocks);17791780 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1781 expect(targetAccountBalance).to.be.equal(validTransferAmount);1782 });17831784 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1785 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1786 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);17871788 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1789 targetAccount.addressRaw,1790 uniqueAssetId,1791 testAmount,1792 );17931794 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1795 targetAccount.addressRaw,1796 {1797 Concrete: {1798 parents: 0,1799 interior: 'Here',1800 },1801 },1802 testAmount,1803 );18041805 let maliciousXcmProgramFullIdSent: any;1806 let maliciousXcmProgramHereIdSent: any;1807 const maxWaitBlocks = 3;18081809 // Try to trick Unique using full UNQ identification1810 await usingAstarPlaygrounds(astarUrl, async (helper) => {1811 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramFullId);18121813 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1814 });18151816 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1817 && event.outcome.isUntrustedReserveLocation);18181819 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1820 expect(accountBalance).to.be.equal(0n);18211822 // Try to trick Unique using shortened UNQ identification1823 await usingAstarPlaygrounds(astarUrl, async (helper) => {1824 await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramHereId);18251826 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1827 });18281829 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1830 && event.outcome.isUntrustedReserveLocation);18311832 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1833 expect(accountBalance).to.be.equal(0n);1834 });1835});