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', [])).toJSON() as [])836 .map(nToBigInt).length != 0;837838 if(!isWhitelisted) {839 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);840 }841842 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);843 });844845 await usingPlaygrounds(async (helper) => {846 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);847 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);848 });849 });850851 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {852853 const destination = {854 V2: {855 parents: 1,856 interior: {857 X1: {858 Parachain: POLKADEX_CHAIN,859 },860 },861 },862 };863864 const beneficiary = {865 V2: {866 parents: 0,867 interior: {868 X1: {869 AccountId32: {870 network: 'Any',871 id: randomAccount.addressRaw,872 },873 },874 },875 },876 };877878 const assets = {879 V2: [880 {881 id: {882 Concrete: {883 parents: 0,884 interior: 'Here',885 },886 },887 fun: {888 Fungible: TRANSFER_AMOUNT,889 },890 },891 ],892 };893894 const feeAssetItem = 0;895896 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');897 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);898 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);899900 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;901 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));902 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;903904 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {905 906907908909910911912913914915 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);916 });917 });918919920 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {921922 const uniqueMultilocation = {923 V2: {924 parents: 1,925 interior: {926 X1: {Parachain: UNIQUE_CHAIN},927 },928 },929 };930931 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(932 randomAccount.addressRaw,933 {934 Concrete: {935 parents: 1,936 interior: {937 X1: {Parachain: UNIQUE_CHAIN},938 },939 },940 },941 TRANSFER_AMOUNT,942 );943944 let xcmProgramSent: any;945946947 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {948 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);949950 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);951 });952953 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);954955 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);956957 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);958 });959960 itSub('Polkadex can send only up to its balance', async ({helper}) => {961 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);962 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);963 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);964 const moreThanPolkadexHas = 2n * polkadexBalance;965966 const targetAccount = helper.arrange.createEmptyAccount();967968 const uniqueMultilocation = {969 V2: {970 parents: 1,971 interior: {972 X1: {Parachain: UNIQUE_CHAIN},973 },974 },975 };976977 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(978 targetAccount.addressRaw,979 {980 Concrete: {981 parents: 0,982 interior: 'Here',983 },984 },985 moreThanPolkadexHas,986 );987988 let maliciousXcmProgramSent: any;989990991 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {992 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);993994 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);995 });996997 await expectFailedToTransact(helper, maliciousXcmProgramSent);998999 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1000 expect(targetAccountBalance).to.be.equal(0n);1001 });10021003 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {1004 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1005 const targetAccount = helper.arrange.createEmptyAccount();10061007 const uniqueMultilocation = {1008 V2: {1009 parents: 1,1010 interior: {1011 X1: {1012 Parachain: UNIQUE_CHAIN,1013 },1014 },1015 },1016 };10171018 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1019 targetAccount.addressRaw,1020 {1021 Concrete: {1022 parents: 1,1023 interior: {1024 X1: {1025 Parachain: UNIQUE_CHAIN,1026 },1027 },1028 },1029 },1030 testAmount,1031 );10321033 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1034 targetAccount.addressRaw,1035 {1036 Concrete: {1037 parents: 0,1038 interior: 'Here',1039 },1040 },1041 testAmount,1042 );10431044 let maliciousXcmProgramFullIdSent: any;1045 let maliciousXcmProgramHereIdSent: any;1046 const maxWaitBlocks = 3;10471048 1049 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1050 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10511052 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1053 });10541055 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10561057 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1058 expect(accountBalance).to.be.equal(0n);10591060 1061 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1062 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10631064 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1065 });10661067 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10681069 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1070 expect(accountBalance).to.be.equal(0n);1071 });1072});1073107410751076describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1077 let alice: IKeyringPair;1078 let alith: IKeyringPair;10791080 const testAmount = 100_000_000_000n;1081 let uniqueParachainJunction;1082 let uniqueAccountJunction;10831084 let uniqueParachainMultilocation: any;1085 let uniqueAccountMultilocation: any;1086 let uniqueCombinedMultilocation: any;1087 let uniqueCombinedMultilocationAcala: any; 10881089 let messageSent: any;10901091 const maxWaitBlocks = 3;10921093 before(async () => {1094 await usingPlaygrounds(async (helper, privateKey) => {1095 alice = await privateKey('//Alice');10961097 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1098 uniqueAccountJunction = {1099 AccountId32: {1100 network: 'Any',1101 id: alice.addressRaw,1102 },1103 };11041105 uniqueParachainMultilocation = {1106 V2: {1107 parents: 1,1108 interior: {1109 X1: uniqueParachainJunction,1110 },1111 },1112 };11131114 uniqueAccountMultilocation = {1115 V2: {1116 parents: 0,1117 interior: {1118 X1: uniqueAccountJunction,1119 },1120 },1121 };11221123 uniqueCombinedMultilocation = {1124 V2: {1125 parents: 1,1126 interior: {1127 X2: [uniqueParachainJunction, uniqueAccountJunction],1128 },1129 },1130 };11311132 uniqueCombinedMultilocationAcala = {1133 V2: {1134 parents: 1,1135 interior: {1136 X2: [uniqueParachainJunction, uniqueAccountJunction],1137 },1138 },1139 };11401141 1142 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1143 });11441145 1146 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1147 alith = helper.account.alithAccount();1148 });1149 });1150115111521153 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1154 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1155 const id = {1156 Token: 'ACA',1157 };1158 const destination = uniqueCombinedMultilocationAcala;1159 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11601161 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1162 });11631164 await expectFailedToTransact(helper, messageSent);1165 });11661167 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1168 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1169 const id = 'SelfReserve';1170 const destination = uniqueCombinedMultilocation;1171 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11721173 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1174 });11751176 await expectFailedToTransact(helper, messageSent);1177 });11781179 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1180 await usingAstarPlaygrounds(astarUrl, async (helper) => {1181 const destinationParachain = uniqueParachainMultilocation;1182 const beneficiary = uniqueAccountMultilocation;1183 const assets = {1184 V2: [{1185 id: {1186 Concrete: {1187 parents: 0,1188 interior: 'Here',1189 },1190 },1191 fun: {1192 Fungible: testAmount,1193 },1194 }],1195 };1196 const feeAssetItem = 0;11971198 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1199 destinationParachain,1200 beneficiary,1201 assets,1202 feeAssetItem,1203 ]);12041205 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1206 });12071208 await expectFailedToTransact(helper, messageSent);1209 });12101211 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12121213 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1214 helper.arrange.createEmptyAccount().addressRaw,1215 {1216 Concrete: {1217 parents: 1,1218 interior: {1219 X1: {1220 Parachain: POLKADEX_CHAIN,1221 },1222 },1223 },1224 },1225 testAmount,1226 );12271228 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1229 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1230 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1231 });12321233 await expectFailedToTransact(helper, messageSent);1234 });1235});12361237describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1238 1239 let alice: IKeyringPair;1240 let uniqueAssetLocation;12411242 let randomAccountUnique: IKeyringPair;1243 let randomAccountMoonbeam: IKeyringPair;12441245 1246 let assetId: string;12471248 const uniqueAssetMetadata = {1249 name: 'xcUnique',1250 symbol: 'xcUNQ',1251 decimals: 18,1252 isFrozen: false,1253 minimalBalance: 1n,1254 };12551256 let balanceUniqueTokenInit: bigint;1257 let balanceUniqueTokenMiddle: bigint;1258 let balanceUniqueTokenFinal: bigint;1259 let balanceForeignUnqTokenInit: bigint;1260 let balanceForeignUnqTokenMiddle: bigint;1261 let balanceForeignUnqTokenFinal: bigint;1262 let balanceGlmrTokenInit: bigint;1263 let balanceGlmrTokenMiddle: bigint;1264 let balanceGlmrTokenFinal: bigint;12651266 before(async () => {1267 await usingPlaygrounds(async (helper, privateKey) => {1268 alice = await privateKey('//Alice');1269 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12701271 balanceForeignUnqTokenInit = 0n;12721273 1274 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1275 });12761277 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1278 const alithAccount = helper.account.alithAccount();1279 const baltatharAccount = helper.account.baltatharAccount();1280 const dorothyAccount = helper.account.dorothyAccount();12811282 randomAccountMoonbeam = helper.account.create();12831284 1285 console.log('Sponsoring Dorothy.......');1286 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1287 console.log('Sponsoring Dorothy.......DONE');1288 12891290 uniqueAssetLocation = {1291 XCM: {1292 parents: 1,1293 interior: {X1: {Parachain: UNIQUE_CHAIN}},1294 },1295 };1296 const existentialDeposit = 1n;1297 const isSufficient = true;1298 const unitsPerSecond = 1n;1299 const numAssetsWeightHint = 0;13001301 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1302 location: uniqueAssetLocation,1303 metadata: uniqueAssetMetadata,1304 existentialDeposit,1305 isSufficient,1306 unitsPerSecond,1307 numAssetsWeightHint,1308 });13091310 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13111312 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13131314 1315 console.log('Acquire Unique AssetId Info on Moonbeam.......');13161317 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13181319 console.log('UNQ asset ID is %s', assetId);1320 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1321 13221323 1324 console.log('Sponsoring random Account.......');1325 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1326 console.log('Sponsoring random Account.......DONE');1327 13281329 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1330 });13311332 await usingPlaygrounds(async (helper) => {1333 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1334 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1335 });1336 });13371338 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1339 const currencyId = {1340 NativeAssetId: 'Here',1341 };1342 const dest = {1343 V2: {1344 parents: 1,1345 interior: {1346 X2: [1347 {Parachain: MOONBEAM_CHAIN},1348 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1349 ],1350 },1351 },1352 };1353 const amount = TRANSFER_AMOUNT;13541355 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13561357 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1358 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13591360 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1361 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1362 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13631364 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1365 await helper.wait.newBlocks(3);13661367 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13681369 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1370 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1371 expect(glmrFees == 0n).to.be.true;13721373 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13741375 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1376 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1377 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1378 });1379 });13801381 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1382 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1383 const asset = {1384 V2: {1385 id: {1386 Concrete: {1387 parents: 1,1388 interior: {1389 X1: {Parachain: UNIQUE_CHAIN},1390 },1391 },1392 },1393 fun: {1394 Fungible: TRANSFER_AMOUNT,1395 },1396 },1397 };1398 const destination = {1399 V2: {1400 parents: 1,1401 interior: {1402 X2: [1403 {Parachain: UNIQUE_CHAIN},1404 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1405 ],1406 },1407 },1408 };14091410 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14111412 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14131414 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1415 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1416 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14171418 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14191420 expect(unqRandomAccountAsset).to.be.null;14211422 balanceForeignUnqTokenFinal = 0n;14231424 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1425 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1426 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1427 });14281429 await helper.wait.newBlocks(3);14301431 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1432 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1433 expect(actuallyDelivered > 0).to.be.true;14341435 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14361437 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1438 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1439 expect(unqFees == 0n).to.be.true;1440 });14411442 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1443 1444 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1445 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1446 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14471448 const moreThanMoonbeamHas = moonbeamBalance * 2n;14491450 let targetAccountBalance = 0n;1451 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14521453 const uniqueMultilocation = {1454 V2: {1455 parents: 1,1456 interior: {1457 X1: {Parachain: UNIQUE_CHAIN},1458 },1459 },1460 };14611462 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1463 targetAccount.addressRaw,1464 {1465 Concrete: {1466 parents: 0,1467 interior: 'Here',1468 },1469 },1470 moreThanMoonbeamHas,1471 );14721473 let maliciousXcmProgramSent: any;1474 const maxWaitBlocks = 3;14751476 1477 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1478 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14791480 1481 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1482 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14831484 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1485 });14861487 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1488 && event.outcome.isFailedToTransactAsset);14891490 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1491 expect(targetAccountBalance).to.be.equal(0n);14921493 1494 const validTransferAmount = moonbeamBalance / 2n;1495 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1496 targetAccount.addressRaw,1497 {1498 Concrete: {1499 parents: 0,1500 interior: 'Here',1501 },1502 },1503 validTransferAmount,1504 );15051506 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1507 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);15081509 1510 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1511 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1512 });15131514 await helper.wait.newBlocks(maxWaitBlocks);15151516 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1517 expect(targetAccountBalance).to.be.equal(validTransferAmount);1518 });15191520 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1521 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1522 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15231524 const uniqueMultilocation = {1525 V2: {1526 parents: 1,1527 interior: {1528 X1: {1529 Parachain: UNIQUE_CHAIN,1530 },1531 },1532 },1533 };15341535 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1536 targetAccount.addressRaw,1537 {1538 Concrete: {1539 parents: 0,1540 interior: {1541 X1: {1542 Parachain: UNIQUE_CHAIN,1543 },1544 },1545 },1546 },1547 testAmount,1548 );15491550 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1551 targetAccount.addressRaw,1552 {1553 Concrete: {1554 parents: 0,1555 interior: 'Here',1556 },1557 },1558 testAmount,1559 );15601561 let maliciousXcmProgramFullIdSent: any;1562 let maliciousXcmProgramHereIdSent: any;1563 const maxWaitBlocks = 3;15641565 1566 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1567 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15681569 1570 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1571 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15721573 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1574 });15751576 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1577 && event.outcome.isUntrustedReserveLocation);15781579 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1580 expect(accountBalance).to.be.equal(0n);15811582 1583 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1584 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15851586 1587 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1588 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15891590 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1591 });15921593 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1594 && event.outcome.isUntrustedReserveLocation);15951596 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1597 expect(accountBalance).to.be.equal(0n);1598 });1599});16001601describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1602 let alice: IKeyringPair;1603 let randomAccount: IKeyringPair;16041605 const UNQ_ASSET_ID_ON_ASTAR = 1;1606 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;16071608 1609 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); 1610 const unitsPerSecond = 228_000_000_000n; 1611 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); 1612 const unqToAstarArrived = 9_999_999_999_088_000_000n; 16131614 1615 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); 1616 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; 16171618 let balanceAfterUniqueToAstarXCM: bigint;16191620 before(async () => {1621 await usingPlaygrounds(async (helper, privateKey) => {1622 alice = await privateKey('//Alice');1623 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1624 console.log('randomAccount', randomAccount.address);16251626 1627 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1628 });16291630 await usingAstarPlaygrounds(astarUrl, async (helper) => {1631 console.log('1. Create foreign asset and metadata');1632 1633 await helper.assets.create(1634 alice,1635 UNQ_ASSET_ID_ON_ASTAR,1636 alice.address,1637 UNQ_MINIMAL_BALANCE_ON_ASTAR,1638 );16391640 await helper.assets.setMetadata(1641 alice,1642 UNQ_ASSET_ID_ON_ASTAR,1643 'Cross chain UNQ',1644 'xcUNQ',1645 Number(UNQ_DECIMALS),1646 );16471648 console.log('2. Register asset location on Astar');1649 const assetLocation = {1650 V2: {1651 parents: 1,1652 interior: {1653 X1: {1654 Parachain: UNIQUE_CHAIN,1655 },1656 },1657 },1658 };16591660 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16611662 console.log('3. Set UNQ payment for XCM execution on Astar');1663 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);16641665 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1666 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1667 });1668 });16691670 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1671 const destination = {1672 V2: {1673 parents: 1,1674 interior: {1675 X1: {1676 Parachain: ASTAR_CHAIN,1677 },1678 },1679 },1680 };16811682 const beneficiary = {1683 V2: {1684 parents: 0,1685 interior: {1686 X1: {1687 AccountId32: {1688 network: 'Any',1689 id: randomAccount.addressRaw,1690 },1691 },1692 },1693 },1694 };16951696 const assets = {1697 V2: [1698 {1699 id: {1700 Concrete: {1701 parents: 0,1702 interior: 'Here',1703 },1704 },1705 fun: {1706 Fungible: unqToAstarTransferred,1707 },1708 },1709 ],1710 };17111712 1713 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1714 console.log(`Initial balance is: ${balanceBefore}`);17151716 const feeAssetItem = 0;1717 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');17181719 1720 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1721 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1722 console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1723 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17241725 await usingAstarPlaygrounds(astarUrl, async (helper) => {1726 await helper.wait.newBlocks(3);1727 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1728 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17291730 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1731 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);17321733 expect(xcUNQbalance).to.eq(unqToAstarArrived);1734 1735 expect(astarBalance).to.eq(astarInitialBalance);1736 });1737 });17381739 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1740 await usingAstarPlaygrounds(astarUrl, async (helper) => {1741 const destination = {1742 V2: {1743 parents: 1,1744 interior: {1745 X1: {1746 Parachain: UNIQUE_CHAIN,1747 },1748 },1749 },1750 };17511752 const beneficiary = {1753 V2: {1754 parents: 0,1755 interior: {1756 X1: {1757 AccountId32: {1758 network: 'Any',1759 id: randomAccount.addressRaw,1760 },1761 },1762 },1763 },1764 };17651766 const assets = {1767 V2: [1768 {1769 id: {1770 Concrete: {1771 parents: 1,1772 interior: {1773 X1: {1774 Parachain: UNIQUE_CHAIN,1775 },1776 },1777 },1778 },1779 fun: {1780 Fungible: unqFromAstarTransfered,1781 },1782 },1783 ],1784 };17851786 1787 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1788 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1789 expect(balanceASTRbefore).to.eq(astarInitialBalance);17901791 const feeAssetItem = 0;1792 1793 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17941795 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1796 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1797 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17981799 1800 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1801 1802 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1803 });18041805 await helper.wait.newBlocks(3);1806 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1807 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1808 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1809 });18101811 itSub('Astar can send only up to its balance', async ({helper}) => {1812 1813 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1814 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1815 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);18161817 const moreThanAstarHas = astarBalance * 2n;18181819 let targetAccountBalance = 0n;1820 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18211822 const uniqueMultilocation = {1823 V2: {1824 parents: 1,1825 interior: {1826 X1: {Parachain: UNIQUE_CHAIN},1827 },1828 },1829 };18301831 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1832 targetAccount.addressRaw,1833 {1834 Concrete: {1835 parents: 0,1836 interior: 'Here',1837 },1838 },1839 moreThanAstarHas,1840 );18411842 let maliciousXcmProgramSent: any;1843 const maxWaitBlocks = 3;18441845 1846 await usingAstarPlaygrounds(astarUrl, async (helper) => {1847 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18481849 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1850 });18511852 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1853 && event.outcome.isFailedToTransactAsset);18541855 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1856 expect(targetAccountBalance).to.be.equal(0n);18571858 1859 const validTransferAmount = astarBalance / 2n;1860 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1861 targetAccount.addressRaw,1862 {1863 Concrete: {1864 parents: 0,1865 interior: 'Here',1866 },1867 },1868 validTransferAmount,1869 );18701871 await usingAstarPlaygrounds(astarUrl, async (helper) => {1872 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1873 });18741875 await helper.wait.newBlocks(maxWaitBlocks);18761877 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1878 expect(targetAccountBalance).to.be.equal(validTransferAmount);1879 });18801881 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1882 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1883 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18841885 const uniqueMultilocation = {1886 V2: {1887 parents: 1,1888 interior: {1889 X1: {1890 Parachain: UNIQUE_CHAIN,1891 },1892 },1893 },1894 };18951896 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1897 targetAccount.addressRaw,1898 {1899 Concrete: {1900 parents: 1,1901 interior: {1902 X1: {1903 Parachain: UNIQUE_CHAIN,1904 },1905 },1906 },1907 },1908 testAmount,1909 );19101911 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1912 targetAccount.addressRaw,1913 {1914 Concrete: {1915 parents: 0,1916 interior: 'Here',1917 },1918 },1919 testAmount,1920 );19211922 let maliciousXcmProgramFullIdSent: any;1923 let maliciousXcmProgramHereIdSent: any;1924 const maxWaitBlocks = 3;19251926 1927 await usingAstarPlaygrounds(astarUrl, async (helper) => {1928 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);19291930 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1931 });19321933 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1934 && event.outcome.isUntrustedReserveLocation);19351936 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1937 expect(accountBalance).to.be.equal(0n);19381939 1940 await usingAstarPlaygrounds(astarUrl, async (helper) => {1941 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19421943 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1944 });19451946 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1947 && event.outcome.isUntrustedReserveLocation);19481949 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1950 expect(accountBalance).to.be.equal(0n);1951 });1952});