1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';21import {nToBigInt} from '@polkadot/util';2223const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);24const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);25const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);26const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);27const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);28const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);2930const STATEMINT_PALLET_INSTANCE = 50;3132const relayUrl = config.relayUrl;33const statemintUrl = config.statemintUrl;34const acalaUrl = config.acalaUrl;35const moonbeamUrl = config.moonbeamUrl;36const astarUrl = config.astarUrl;37const polkadexUrl = config.polkadexUrl;3839const RELAY_DECIMALS = 12;40const STATEMINT_DECIMALS = 12;41const ACALA_DECIMALS = 12;42const ASTAR_DECIMALS = 18n;43const UNQ_DECIMALS = 18n;4445const TRANSFER_AMOUNT = 2000000000000000000000000n;4647const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4849const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;5051const USDT_ASSET_ID = 100;52const USDT_ASSET_METADATA_DECIMALS = 18;53const USDT_ASSET_METADATA_NAME = 'USDT';54const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';55const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;56const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5758const SAFE_XCM_VERSION = 2;59const maxWaitBlocks = 6;60const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {61 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash62 && event.outcome.isFailedToTransactAsset);63};64const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {65 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash66 && event.outcome.isUntrustedReserveLocation);67};68describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {69 let alice: IKeyringPair;70 let bob: IKeyringPair;7172 let balanceStmnBefore: bigint;73 let balanceStmnAfter: bigint;7475 let balanceUniqueBefore: bigint;76 let balanceUniqueAfter: bigint;77 let balanceUniqueFinal: bigint;7879 let balanceBobBefore: bigint;80 let balanceBobAfter: bigint;81 let balanceBobFinal: bigint;8283 let balanceBobRelayTokenBefore: bigint;84 let balanceBobRelayTokenAfter: bigint;858687 before(async () => {88 await usingPlaygrounds(async (helper, privateKey) => {89 alice = await privateKey('//Alice');90 bob = await privateKey('//Bob'); 9192 93 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);94 });9596 await usingRelayPlaygrounds(relayUrl, async (helper) => {97 98 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);99 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);100 });101102 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {103 const sovereignFundingAmount = 3_500_000_000n;104105 await helper.assets.create(106 alice,107 USDT_ASSET_ID,108 alice.address,109 USDT_ASSET_METADATA_MINIMAL_BALANCE,110 );111 await helper.assets.setMetadata(112 alice,113 USDT_ASSET_ID,114 USDT_ASSET_METADATA_NAME,115 USDT_ASSET_METADATA_DESCRIPTION,116 USDT_ASSET_METADATA_DECIMALS,117 );118 await helper.assets.mint(119 alice,120 USDT_ASSET_ID,121 alice.address,122 USDT_ASSET_AMOUNT,123 );124125 126 127 128 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);129 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);130 });131132133 await usingPlaygrounds(async (helper) => {134 const location = {135 V2: {136 parents: 1,137 interior: {X3: [138 {139 Parachain: STATEMINT_CHAIN,140 },141 {142 PalletInstance: STATEMINT_PALLET_INSTANCE,143 },144 {145 GeneralIndex: USDT_ASSET_ID,146 },147 ]},148 },149 };150151 const metadata =152 {153 name: USDT_ASSET_ID,154 symbol: USDT_ASSET_METADATA_NAME,155 decimals: USDT_ASSET_METADATA_DECIMALS,156 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,157 };158 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);159 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);160 });161162163 164 165 await usingRelayPlaygrounds(relayUrl, async (helper) => {166 const destination = {167 V2: {168 parents: 0,169 interior: {X1: {170 Parachain: UNIQUE_CHAIN,171 },172 },173 }};174175 const beneficiary = {176 V2: {177 parents: 0,178 interior: {X1: {179 AccountId32: {180 network: 'Any',181 id: alice.addressRaw,182 },183 }},184 },185 };186187 const assets = {188 V2: [189 {190 id: {191 Concrete: {192 parents: 0,193 interior: 'Here',194 },195 },196 fun: {197 Fungible: TRANSFER_AMOUNT_RELAY,198 },199 },200 ],201 };202203 const feeAssetItem = 0;204205 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');206 });207208 });209210 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {211 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {212 const dest = {213 V2: {214 parents: 1,215 interior: {X1: {216 Parachain: UNIQUE_CHAIN,217 },218 },219 }};220221 const beneficiary = {222 V2: {223 parents: 0,224 interior: {X1: {225 AccountId32: {226 network: 'Any',227 id: alice.addressRaw,228 },229 }},230 },231 };232233 const assets = {234 V2: [235 {236 id: {237 Concrete: {238 parents: 0,239 interior: {240 X2: [241 {242 PalletInstance: STATEMINT_PALLET_INSTANCE,243 },244 {245 GeneralIndex: USDT_ASSET_ID,246 },247 ]},248 },249 },250 fun: {251 Fungible: TRANSFER_AMOUNT,252 },253 },254 ],255 };256257 const feeAssetItem = 0;258259 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);260 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');261262 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);263264 265 console.log(266 '[Statemint -> Unique] transaction fees on Statemint: %s WND',267 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),268 );269 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;270271 });272273274 275 await helper.wait.newBlocks(3);276277 278 const free = await helper.ft.getBalance(1, {Substrate: alice.address});279280 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);281282 console.log(283 '[Statemint -> Unique] transaction fees on Unique: %s USDT',284 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),285 );286 console.log(287 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',288 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),289 );290 291 expect(free).to.be.equal(TRANSFER_AMOUNT);292 293 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;294 });295296 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {297 const destination = {298 V2: {299 parents: 1,300 interior: {X2: [301 {302 Parachain: STATEMINT_CHAIN,303 },304 {305 AccountId32: {306 network: 'Any',307 id: alice.addressRaw,308 },309 },310 ]},311 },312 };313314 const relayFee = 400_000_000_000_000n;315 const currencies: [any, bigint][] = [316 [317 {318 ForeignAssetId: 0,319 },320 TRANSFER_AMOUNT,321 ],322 [323 {324 NativeAssetId: 'Parent',325 },326 relayFee,327 ],328 ];329330 const feeItem = 1;331332 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');333334 335 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);336 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));337 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;338339 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {340 await helper.wait.newBlocks(3);341342 343 344 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;345 });346 });347348 itSub('Should connect and send Relay token to Unique', async ({helper}) => {349 balanceBobBefore = await helper.balance.getSubstrate(bob.address);350 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});351352 await usingRelayPlaygrounds(relayUrl, async (helper) => {353 const destination = {354 V2: {355 parents: 0,356 interior: {X1: {357 Parachain: UNIQUE_CHAIN,358 },359 },360 }};361362 const beneficiary = {363 V2: {364 parents: 0,365 interior: {X1: {366 AccountId32: {367 network: 'Any',368 id: bob.addressRaw,369 },370 }},371 },372 };373374 const assets = {375 V2: [376 {377 id: {378 Concrete: {379 parents: 0,380 interior: 'Here',381 },382 },383 fun: {384 Fungible: TRANSFER_AMOUNT_RELAY,385 },386 },387 ],388 };389390 const feeAssetItem = 0;391392 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');393 });394395 await helper.wait.newBlocks(3);396397 balanceBobAfter = await helper.balance.getSubstrate(bob.address);398 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});399400 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;401 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;402 console.log(403 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',404 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),405 );406 console.log(407 '[Relay (Westend) -> Unique] transaction fees: %s WND',408 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),409 );410 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);411 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;412 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;413 });414415 itSub('Should connect and send Relay token back', async ({helper}) => {416 let relayTokenBalanceBefore: bigint;417 let relayTokenBalanceAfter: bigint;418 await usingRelayPlaygrounds(relayUrl, async (helper) => {419 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);420 });421422 const destination = {423 V2: {424 parents: 1,425 interior: {426 X1:{427 AccountId32: {428 network: 'Any',429 id: bob.addressRaw,430 },431 },432 },433 },434 };435436 const currencies: any = [437 [438 {439 NativeAssetId: 'Parent',440 },441 TRANSFER_AMOUNT_RELAY,442 ],443 ];444445 const feeItem = 0;446447 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');448449 balanceBobFinal = await helper.balance.getSubstrate(bob.address);450 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));451452 await usingRelayPlaygrounds(relayUrl, async (helper) => {453 await helper.wait.newBlocks(10);454 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);455456 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;457 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));458 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;459 });460 });461});462463describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {464 let alice: IKeyringPair;465 let randomAccount: IKeyringPair;466467 let balanceUniqueTokenInit: bigint;468 let balanceUniqueTokenMiddle: bigint;469 let balanceUniqueTokenFinal: bigint;470 let balanceAcalaTokenInit: bigint;471 let balanceAcalaTokenMiddle: bigint;472 let balanceAcalaTokenFinal: bigint;473 let balanceUniqueForeignTokenInit: bigint;474 let balanceUniqueForeignTokenMiddle: bigint;475 let balanceUniqueForeignTokenFinal: bigint;476477 478 479 480 const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;481 const acalaEps = 8n * 10n ** 16n;482483 let acalaBackwardTransferAmount: bigint;484485 before(async () => {486 await usingPlaygrounds(async (helper, privateKey) => {487 alice = await privateKey('//Alice');488 [randomAccount] = await helper.arrange.createAccounts([0n], alice);489490 491 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);492 });493494 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {495 const destination = {496 V2: {497 parents: 1,498 interior: {499 X1: {500 Parachain: UNIQUE_CHAIN,501 },502 },503 },504 };505506 const metadata = {507 name: 'Unique Network',508 symbol: 'UNQ',509 decimals: 18,510 minimalBalance: 1250000000000000000n,511 };512513 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);514 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);515 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);516 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});517 });518519 await usingPlaygrounds(async (helper) => {520 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);521 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);522 });523 });524525 itSub('Should connect and send UNQ to Acala', async ({helper}) => {526527 const destination = {528 V2: {529 parents: 1,530 interior: {531 X1: {532 Parachain: ACALA_CHAIN,533 },534 },535 },536 };537538 const beneficiary = {539 V2: {540 parents: 0,541 interior: {542 X1: {543 AccountId32: {544 network: 'Any',545 id: randomAccount.addressRaw,546 },547 },548 },549 },550 };551552 const assets = {553 V2: [554 {555 id: {556 Concrete: {557 parents: 0,558 interior: 'Here',559 },560 },561 fun: {562 Fungible: TRANSFER_AMOUNT,563 },564 },565 ],566 };567568 const feeAssetItem = 0;569570 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');571 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);572573 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;574 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));575 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;576577 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {578 await helper.wait.newBlocks(3);579580 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});581 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);582583 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;584 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;585 acalaBackwardTransferAmount = unqIncomeTransfer;586587 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;588589 console.log(590 '[Unique -> Acala] transaction fees on Acala: %s ACA',591 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),592 );593 console.log(594 '[Unique -> Acala] transaction fees on Acala: %s UNQ',595 helper.util.bigIntToDecimals(acaUnqFees),596 );597 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));598 expect(acaFees == 0n).to.be.true;599600 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;601602 expect(603 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,604 'Acala took different income fee, check the Acala foreign asset config',605 ).to.be.true;606 });607 });608609 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {610 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {611 const destination = {612 V2: {613 parents: 1,614 interior: {615 X2: [616 {Parachain: UNIQUE_CHAIN},617 {618 AccountId32: {619 network: 'Any',620 id: randomAccount.addressRaw,621 },622 },623 ],624 },625 },626 };627628 const id = {629 ForeignAsset: 0,630 };631632 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');633 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);634 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);635636 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;637 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;638639 console.log(640 '[Acala -> Unique] transaction fees on Acala: %s ACA',641 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),642 );643 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));644645 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;646 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;647 });648649 await helper.wait.newBlocks(3);650651 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);652 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;653 expect(actuallyDelivered > 0).to.be.true;654655 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));656657 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;658 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));659 expect(unqFees == 0n).to.be.true;660 });661662 itSub('Acala can send only up to its balance', async ({helper}) => {663 664 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);665 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);666 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);667668 const moreThanAcalaHas = acalaBalance * 2n;669670 let targetAccountBalance = 0n;671 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);672673 const uniqueMultilocation = {674 V2: {675 parents: 1,676 interior: {677 X1: {Parachain: UNIQUE_CHAIN},678 },679 },680 };681682 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(683 targetAccount.addressRaw,684 {685 Concrete: {686 parents: 0,687 interior: 'Here',688 },689 },690 moreThanAcalaHas,691 );692693 let maliciousXcmProgramSent: any;694 const maxWaitBlocks = 3;695696 697 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {698 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);699700 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);701 });702703 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash704 && event.outcome.isFailedToTransactAsset);705706 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);707 expect(targetAccountBalance).to.be.equal(0n);708709 710 const validTransferAmount = acalaBalance / 2n;711 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(712 targetAccount.addressRaw,713 {714 Concrete: {715 parents: 0,716 interior: 'Here',717 },718 },719 validTransferAmount,720 );721722 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {723 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);724 });725726 await helper.wait.newBlocks(maxWaitBlocks);727728 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);729 expect(targetAccountBalance).to.be.equal(validTransferAmount);730 });731732 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {733 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);734 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);735736 const uniqueMultilocation = {737 V2: {738 parents: 1,739 interior: {740 X1: {741 Parachain: UNIQUE_CHAIN,742 },743 },744 },745 };746747 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(748 targetAccount.addressRaw,749 {750 Concrete: {751 parents: 1,752 interior: {753 X1: {754 Parachain: UNIQUE_CHAIN,755 },756 },757 },758 },759 testAmount,760 );761762 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(763 targetAccount.addressRaw,764 {765 Concrete: {766 parents: 0,767 interior: 'Here',768 },769 },770 testAmount,771 );772773 let maliciousXcmProgramFullIdSent: any;774 let maliciousXcmProgramHereIdSent: any;775 const maxWaitBlocks = 3;776777 778 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {779 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);780781 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);782 });783784 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash785 && event.outcome.isUntrustedReserveLocation);786787 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);788 expect(accountBalance).to.be.equal(0n);789790 791 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {792 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);793794 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);795 });796797 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash798 && event.outcome.isUntrustedReserveLocation);799800 accountBalance = await helper.balance.getSubstrate(targetAccount.address);801 expect(accountBalance).to.be.equal(0n);802 });803});804805describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {806 let alice: IKeyringPair;807 let randomAccount: IKeyringPair;808 let unqFees: bigint;809 let balanceUniqueTokenInit: bigint;810 let balanceUniqueTokenMiddle: bigint;811 let balanceUniqueTokenFinal: bigint;812 const maxWaitBlocks = 6;813814 const uniqueAssetId = {815 Concrete: {816 parents: 1,817 interior: {818 X1: {819 Parachain: UNIQUE_CHAIN,820 },821 },822 },823 };824825 before(async () => {826 await usingPlaygrounds(async (helper, privateKey) => {827 alice = await privateKey('//Alice');828 [randomAccount] = await helper.arrange.createAccounts([0n], alice);829830 831 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);832 });833834 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {835 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))836 .toJSON() as [])837 .map(nToBigInt).length != 0;838 839840841842843844 if(!isWhitelisted) {845 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);846 }847848 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);849 });850851 await usingPlaygrounds(async (helper) => {852 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);853 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);854 });855 });856857 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {858859 const destination = {860 V2: {861 parents: 1,862 interior: {863 X1: {864 Parachain: POLKADEX_CHAIN,865 },866 },867 },868 };869870 const beneficiary = {871 V2: {872 parents: 0,873 interior: {874 X1: {875 AccountId32: {876 network: 'Any',877 id: randomAccount.addressRaw,878 },879 },880 },881 },882 };883884 const assets = {885 V2: [886 {887 id: {888 Concrete: {889 parents: 0,890 interior: 'Here',891 },892 },893 fun: {894 Fungible: TRANSFER_AMOUNT,895 },896 },897 ],898 };899900 const feeAssetItem = 0;901902 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');903 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);904 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);905906 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;907 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));908 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;909910 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {911 912913914915916917918919920921 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);922 });923 });924925926 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {927928 const uniqueMultilocation = {929 V2: {930 parents: 1,931 interior: {932 X1: {Parachain: UNIQUE_CHAIN},933 },934 },935 };936937 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(938 randomAccount.addressRaw,939 {940 Concrete: {941 parents: 1,942 interior: {943 X1: {Parachain: UNIQUE_CHAIN},944 },945 },946 },947 TRANSFER_AMOUNT,948 );949950 let xcmProgramSent: any;951952953 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {954 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);955956 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);957 });958959 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);960961 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);962963 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);964 });965966 itSub('Polkadex can send only up to its balance', async ({helper}) => {967 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);968 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);969 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);970 const moreThanPolkadexHas = 2n * polkadexBalance;971972 const targetAccount = helper.arrange.createEmptyAccount();973974 const uniqueMultilocation = {975 V2: {976 parents: 1,977 interior: {978 X1: {Parachain: UNIQUE_CHAIN},979 },980 },981 };982983 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(984 targetAccount.addressRaw,985 {986 Concrete: {987 parents: 0,988 interior: 'Here',989 },990 },991 moreThanPolkadexHas,992 );993994 let maliciousXcmProgramSent: any;995996997 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {998 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);9991000 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1001 });10021003 await expectFailedToTransact(helper, maliciousXcmProgramSent);10041005 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1006 expect(targetAccountBalance).to.be.equal(0n);1007 });10081009 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {1010 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1011 const targetAccount = helper.arrange.createEmptyAccount();10121013 const uniqueMultilocation = {1014 V2: {1015 parents: 1,1016 interior: {1017 X1: {1018 Parachain: UNIQUE_CHAIN,1019 },1020 },1021 },1022 };10231024 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1025 targetAccount.addressRaw,1026 {1027 Concrete: {1028 parents: 1,1029 interior: {1030 X1: {1031 Parachain: UNIQUE_CHAIN,1032 },1033 },1034 },1035 },1036 testAmount,1037 );10381039 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1040 targetAccount.addressRaw,1041 {1042 Concrete: {1043 parents: 0,1044 interior: 'Here',1045 },1046 },1047 testAmount,1048 );10491050 let maliciousXcmProgramFullIdSent: any;1051 let maliciousXcmProgramHereIdSent: any;1052 const maxWaitBlocks = 3;10531054 1055 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1056 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10571058 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1059 });10601061 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10621063 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1064 expect(accountBalance).to.be.equal(0n);10651066 1067 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1068 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10691070 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1071 });10721073 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10741075 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1076 expect(accountBalance).to.be.equal(0n);1077 });1078});1079108010811082describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1083 let alice: IKeyringPair;1084 let alith: IKeyringPair;10851086 const testAmount = 100_000_000_000n;1087 let uniqueParachainJunction;1088 let uniqueAccountJunction;10891090 let uniqueParachainMultilocation: any;1091 let uniqueAccountMultilocation: any;1092 let uniqueCombinedMultilocation: any;1093 let uniqueCombinedMultilocationAcala: any; 10941095 let messageSent: any;10961097 const maxWaitBlocks = 3;10981099 before(async () => {1100 await usingPlaygrounds(async (helper, privateKey) => {1101 alice = await privateKey('//Alice');11021103 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1104 uniqueAccountJunction = {1105 AccountId32: {1106 network: 'Any',1107 id: alice.addressRaw,1108 },1109 };11101111 uniqueParachainMultilocation = {1112 V2: {1113 parents: 1,1114 interior: {1115 X1: uniqueParachainJunction,1116 },1117 },1118 };11191120 uniqueAccountMultilocation = {1121 V2: {1122 parents: 0,1123 interior: {1124 X1: uniqueAccountJunction,1125 },1126 },1127 };11281129 uniqueCombinedMultilocation = {1130 V2: {1131 parents: 1,1132 interior: {1133 X2: [uniqueParachainJunction, uniqueAccountJunction],1134 },1135 },1136 };11371138 uniqueCombinedMultilocationAcala = {1139 V2: {1140 parents: 1,1141 interior: {1142 X2: [uniqueParachainJunction, uniqueAccountJunction],1143 },1144 },1145 };11461147 1148 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1149 });11501151 1152 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1153 alith = helper.account.alithAccount();1154 });1155 });1156115711581159 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1160 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1161 const id = {1162 Token: 'ACA',1163 };1164 const destination = uniqueCombinedMultilocationAcala;1165 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11661167 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1168 });11691170 await expectFailedToTransact(helper, messageSent);1171 });11721173 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1174 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1175 const id = 'SelfReserve';1176 const destination = uniqueCombinedMultilocation;1177 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11781179 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1180 });11811182 await expectFailedToTransact(helper, messageSent);1183 });11841185 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1186 await usingAstarPlaygrounds(astarUrl, async (helper) => {1187 const destinationParachain = uniqueParachainMultilocation;1188 const beneficiary = uniqueAccountMultilocation;1189 const assets = {1190 V2: [{1191 id: {1192 Concrete: {1193 parents: 0,1194 interior: 'Here',1195 },1196 },1197 fun: {1198 Fungible: testAmount,1199 },1200 }],1201 };1202 const feeAssetItem = 0;12031204 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1205 destinationParachain,1206 beneficiary,1207 assets,1208 feeAssetItem,1209 ]);12101211 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1212 });12131214 await expectFailedToTransact(helper, messageSent);1215 });12161217 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12181219 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1220 helper.arrange.createEmptyAccount().addressRaw,1221 {1222 Concrete: {1223 parents: 1,1224 interior: {1225 X1: {1226 Parachain: POLKADEX_CHAIN,1227 },1228 },1229 },1230 },1231 testAmount,1232 );12331234 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1235 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1236 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1237 });12381239 await expectFailedToTransact(helper, messageSent);1240 });1241});12421243describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1244 1245 let alice: IKeyringPair;1246 let uniqueAssetLocation;12471248 let randomAccountUnique: IKeyringPair;1249 let randomAccountMoonbeam: IKeyringPair;12501251 1252 let assetId: string;12531254 const uniqueAssetMetadata = {1255 name: 'xcUnique',1256 symbol: 'xcUNQ',1257 decimals: 18,1258 isFrozen: false,1259 minimalBalance: 1n,1260 };12611262 let balanceUniqueTokenInit: bigint;1263 let balanceUniqueTokenMiddle: bigint;1264 let balanceUniqueTokenFinal: bigint;1265 let balanceForeignUnqTokenInit: bigint;1266 let balanceForeignUnqTokenMiddle: bigint;1267 let balanceForeignUnqTokenFinal: bigint;1268 let balanceGlmrTokenInit: bigint;1269 let balanceGlmrTokenMiddle: bigint;1270 let balanceGlmrTokenFinal: bigint;12711272 before(async () => {1273 await usingPlaygrounds(async (helper, privateKey) => {1274 alice = await privateKey('//Alice');1275 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12761277 balanceForeignUnqTokenInit = 0n;12781279 1280 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1281 });12821283 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1284 const alithAccount = helper.account.alithAccount();1285 const baltatharAccount = helper.account.baltatharAccount();1286 const dorothyAccount = helper.account.dorothyAccount();12871288 randomAccountMoonbeam = helper.account.create();12891290 1291 console.log('Sponsoring Dorothy.......');1292 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1293 console.log('Sponsoring Dorothy.......DONE');1294 12951296 uniqueAssetLocation = {1297 XCM: {1298 parents: 1,1299 interior: {X1: {Parachain: UNIQUE_CHAIN}},1300 },1301 };1302 const existentialDeposit = 1n;1303 const isSufficient = true;1304 const unitsPerSecond = 1n;1305 const numAssetsWeightHint = 0;13061307 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1308 location: uniqueAssetLocation,1309 metadata: uniqueAssetMetadata,1310 existentialDeposit,1311 isSufficient,1312 unitsPerSecond,1313 numAssetsWeightHint,1314 });13151316 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13171318 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13191320 1321 console.log('Acquire Unique AssetId Info on Moonbeam.......');13221323 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13241325 console.log('UNQ asset ID is %s', assetId);1326 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1327 13281329 1330 console.log('Sponsoring random Account.......');1331 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1332 console.log('Sponsoring random Account.......DONE');1333 13341335 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1336 });13371338 await usingPlaygrounds(async (helper) => {1339 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1340 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1341 });1342 });13431344 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1345 const currencyId = {1346 NativeAssetId: 'Here',1347 };1348 const dest = {1349 V2: {1350 parents: 1,1351 interior: {1352 X2: [1353 {Parachain: MOONBEAM_CHAIN},1354 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1355 ],1356 },1357 },1358 };1359 const amount = TRANSFER_AMOUNT;13601361 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13621363 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1364 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13651366 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1367 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1368 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13691370 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1371 await helper.wait.newBlocks(3);13721373 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13741375 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1376 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1377 expect(glmrFees == 0n).to.be.true;13781379 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13801381 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1382 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1383 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1384 });1385 });13861387 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1388 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1389 const asset = {1390 V2: {1391 id: {1392 Concrete: {1393 parents: 1,1394 interior: {1395 X1: {Parachain: UNIQUE_CHAIN},1396 },1397 },1398 },1399 fun: {1400 Fungible: TRANSFER_AMOUNT,1401 },1402 },1403 };1404 const destination = {1405 V2: {1406 parents: 1,1407 interior: {1408 X2: [1409 {Parachain: UNIQUE_CHAIN},1410 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1411 ],1412 },1413 },1414 };14151416 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14171418 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14191420 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1421 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1422 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14231424 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14251426 expect(unqRandomAccountAsset).to.be.null;14271428 balanceForeignUnqTokenFinal = 0n;14291430 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1431 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1432 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1433 });14341435 await helper.wait.newBlocks(3);14361437 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1438 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1439 expect(actuallyDelivered > 0).to.be.true;14401441 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14421443 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1444 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1445 expect(unqFees == 0n).to.be.true;1446 });14471448 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1449 1450 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1451 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1452 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14531454 const moreThanMoonbeamHas = moonbeamBalance * 2n;14551456 let targetAccountBalance = 0n;1457 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14581459 const uniqueMultilocation = {1460 V2: {1461 parents: 1,1462 interior: {1463 X1: {Parachain: UNIQUE_CHAIN},1464 },1465 },1466 };14671468 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1469 targetAccount.addressRaw,1470 {1471 Concrete: {1472 parents: 0,1473 interior: 'Here',1474 },1475 },1476 moreThanMoonbeamHas,1477 );14781479 let maliciousXcmProgramSent: any;1480 const maxWaitBlocks = 3;14811482 1483 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1484 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14851486 1487 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1488 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14891490 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1491 });14921493 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1494 && event.outcome.isFailedToTransactAsset);14951496 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1497 expect(targetAccountBalance).to.be.equal(0n);14981499 1500 const validTransferAmount = moonbeamBalance / 2n;1501 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1502 targetAccount.addressRaw,1503 {1504 Concrete: {1505 parents: 0,1506 interior: 'Here',1507 },1508 },1509 validTransferAmount,1510 );15111512 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1513 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);15141515 1516 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1517 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1518 });15191520 await helper.wait.newBlocks(maxWaitBlocks);15211522 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1523 expect(targetAccountBalance).to.be.equal(validTransferAmount);1524 });15251526 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1527 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1528 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15291530 const uniqueMultilocation = {1531 V2: {1532 parents: 1,1533 interior: {1534 X1: {1535 Parachain: UNIQUE_CHAIN,1536 },1537 },1538 },1539 };15401541 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1542 targetAccount.addressRaw,1543 {1544 Concrete: {1545 parents: 0,1546 interior: {1547 X1: {1548 Parachain: UNIQUE_CHAIN,1549 },1550 },1551 },1552 },1553 testAmount,1554 );15551556 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1557 targetAccount.addressRaw,1558 {1559 Concrete: {1560 parents: 0,1561 interior: 'Here',1562 },1563 },1564 testAmount,1565 );15661567 let maliciousXcmProgramFullIdSent: any;1568 let maliciousXcmProgramHereIdSent: any;1569 const maxWaitBlocks = 3;15701571 1572 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1573 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15741575 1576 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1577 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15781579 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1580 });15811582 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1583 && event.outcome.isUntrustedReserveLocation);15841585 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1586 expect(accountBalance).to.be.equal(0n);15871588 1589 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1590 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15911592 1593 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1594 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15951596 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1597 });15981599 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1600 && event.outcome.isUntrustedReserveLocation);16011602 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1603 expect(accountBalance).to.be.equal(0n);1604 });1605});16061607describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1608 let alice: IKeyringPair;1609 let randomAccount: IKeyringPair;16101611 const UNQ_ASSET_ID_ON_ASTAR = 1;1612 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;16131614 1615 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); 1616 const unitsPerSecond = 228_000_000_000n; 1617 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); 1618 const unqToAstarArrived = 9_999_999_999_088_000_000n; 16191620 1621 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); 1622 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; 16231624 let balanceAfterUniqueToAstarXCM: bigint;16251626 before(async () => {1627 await usingPlaygrounds(async (helper, privateKey) => {1628 alice = await privateKey('//Alice');1629 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1630 console.log('randomAccount', randomAccount.address);16311632 1633 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1634 });16351636 await usingAstarPlaygrounds(astarUrl, async (helper) => {1637 console.log('1. Create foreign asset and metadata');1638 1639 await helper.assets.create(1640 alice,1641 UNQ_ASSET_ID_ON_ASTAR,1642 alice.address,1643 UNQ_MINIMAL_BALANCE_ON_ASTAR,1644 );16451646 await helper.assets.setMetadata(1647 alice,1648 UNQ_ASSET_ID_ON_ASTAR,1649 'Cross chain UNQ',1650 'xcUNQ',1651 Number(UNQ_DECIMALS),1652 );16531654 console.log('2. Register asset location on Astar');1655 const assetLocation = {1656 V2: {1657 parents: 1,1658 interior: {1659 X1: {1660 Parachain: UNIQUE_CHAIN,1661 },1662 },1663 },1664 };16651666 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16671668 console.log('3. Set UNQ payment for XCM execution on Astar');1669 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);16701671 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1672 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1673 });1674 });16751676 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1677 const destination = {1678 V2: {1679 parents: 1,1680 interior: {1681 X1: {1682 Parachain: ASTAR_CHAIN,1683 },1684 },1685 },1686 };16871688 const beneficiary = {1689 V2: {1690 parents: 0,1691 interior: {1692 X1: {1693 AccountId32: {1694 network: 'Any',1695 id: randomAccount.addressRaw,1696 },1697 },1698 },1699 },1700 };17011702 const assets = {1703 V2: [1704 {1705 id: {1706 Concrete: {1707 parents: 0,1708 interior: 'Here',1709 },1710 },1711 fun: {1712 Fungible: unqToAstarTransferred,1713 },1714 },1715 ],1716 };17171718 1719 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1720 console.log(`Initial balance is: ${balanceBefore}`);17211722 const feeAssetItem = 0;1723 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');17241725 1726 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1727 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1728 console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1729 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17301731 await usingAstarPlaygrounds(astarUrl, async (helper) => {1732 await helper.wait.newBlocks(3);1733 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1734 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17351736 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1737 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);17381739 expect(xcUNQbalance).to.eq(unqToAstarArrived);1740 1741 expect(astarBalance).to.eq(astarInitialBalance);1742 });1743 });17441745 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1746 await usingAstarPlaygrounds(astarUrl, async (helper) => {1747 const destination = {1748 V2: {1749 parents: 1,1750 interior: {1751 X1: {1752 Parachain: UNIQUE_CHAIN,1753 },1754 },1755 },1756 };17571758 const beneficiary = {1759 V2: {1760 parents: 0,1761 interior: {1762 X1: {1763 AccountId32: {1764 network: 'Any',1765 id: randomAccount.addressRaw,1766 },1767 },1768 },1769 },1770 };17711772 const assets = {1773 V2: [1774 {1775 id: {1776 Concrete: {1777 parents: 1,1778 interior: {1779 X1: {1780 Parachain: UNIQUE_CHAIN,1781 },1782 },1783 },1784 },1785 fun: {1786 Fungible: unqFromAstarTransfered,1787 },1788 },1789 ],1790 };17911792 1793 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1794 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1795 expect(balanceASTRbefore).to.eq(astarInitialBalance);17961797 const feeAssetItem = 0;1798 1799 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);18001801 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1802 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1803 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);18041805 1806 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1807 1808 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1809 });18101811 await helper.wait.newBlocks(3);1812 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1813 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1814 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1815 });18161817 itSub('Astar can send only up to its balance', async ({helper}) => {1818 1819 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1820 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1821 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);18221823 const moreThanAstarHas = astarBalance * 2n;18241825 let targetAccountBalance = 0n;1826 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18271828 const uniqueMultilocation = {1829 V2: {1830 parents: 1,1831 interior: {1832 X1: {Parachain: UNIQUE_CHAIN},1833 },1834 },1835 };18361837 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1838 targetAccount.addressRaw,1839 {1840 Concrete: {1841 parents: 0,1842 interior: 'Here',1843 },1844 },1845 moreThanAstarHas,1846 );18471848 let maliciousXcmProgramSent: any;1849 const maxWaitBlocks = 3;18501851 1852 await usingAstarPlaygrounds(astarUrl, async (helper) => {1853 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18541855 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1856 });18571858 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1859 && event.outcome.isFailedToTransactAsset);18601861 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1862 expect(targetAccountBalance).to.be.equal(0n);18631864 1865 const validTransferAmount = astarBalance / 2n;1866 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1867 targetAccount.addressRaw,1868 {1869 Concrete: {1870 parents: 0,1871 interior: 'Here',1872 },1873 },1874 validTransferAmount,1875 );18761877 await usingAstarPlaygrounds(astarUrl, async (helper) => {1878 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1879 });18801881 await helper.wait.newBlocks(maxWaitBlocks);18821883 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1884 expect(targetAccountBalance).to.be.equal(validTransferAmount);1885 });18861887 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1888 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1889 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18901891 const uniqueMultilocation = {1892 V2: {1893 parents: 1,1894 interior: {1895 X1: {1896 Parachain: UNIQUE_CHAIN,1897 },1898 },1899 },1900 };19011902 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1903 targetAccount.addressRaw,1904 {1905 Concrete: {1906 parents: 1,1907 interior: {1908 X1: {1909 Parachain: UNIQUE_CHAIN,1910 },1911 },1912 },1913 },1914 testAmount,1915 );19161917 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1918 targetAccount.addressRaw,1919 {1920 Concrete: {1921 parents: 0,1922 interior: 'Here',1923 },1924 },1925 testAmount,1926 );19271928 let maliciousXcmProgramFullIdSent: any;1929 let maliciousXcmProgramHereIdSent: any;1930 const maxWaitBlocks = 3;19311932 1933 await usingAstarPlaygrounds(astarUrl, async (helper) => {1934 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);19351936 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1937 });19381939 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1940 && event.outcome.isUntrustedReserveLocation);19411942 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1943 expect(accountBalance).to.be.equal(0n);19441945 1946 await usingAstarPlaygrounds(astarUrl, async (helper) => {1947 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19481949 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1950 });19511952 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1953 && event.outcome.isUntrustedReserveLocation);19541955 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1956 expect(accountBalance).to.be.equal(0n);1957 });1958});