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;837 838839840841842 if(!isWhitelisted) {843 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);844 }845846 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);847 });848849 await usingPlaygrounds(async (helper) => {850 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);851 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);852 });853 });854855 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {856857 const destination = {858 V2: {859 parents: 1,860 interior: {861 X1: {862 Parachain: POLKADEX_CHAIN,863 },864 },865 },866 };867868 const beneficiary = {869 V2: {870 parents: 0,871 interior: {872 X1: {873 AccountId32: {874 network: 'Any',875 id: randomAccount.addressRaw,876 },877 },878 },879 },880 };881882 const assets = {883 V2: [884 {885 id: {886 Concrete: {887 parents: 0,888 interior: 'Here',889 },890 },891 fun: {892 Fungible: TRANSFER_AMOUNT,893 },894 },895 ],896 };897898 const feeAssetItem = 0;899900 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');901 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);902 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);903904 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;905 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));906 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;907908 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {909 910911912913914915916917918919 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);920 });921 });922923924 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {925926 const uniqueMultilocation = {927 V2: {928 parents: 1,929 interior: {930 X1: {Parachain: UNIQUE_CHAIN},931 },932 },933 };934935 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(936 randomAccount.addressRaw,937 {938 Concrete: {939 parents: 1,940 interior: {941 X1: {Parachain: UNIQUE_CHAIN},942 },943 },944 },945 TRANSFER_AMOUNT,946 );947948 let xcmProgramSent: any;949950951 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {952 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);953954 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);955 });956957 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);958959 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);960961 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);962 });963964 itSub('Polkadex can send only up to its balance', async ({helper}) => {965 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);966 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);967 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);968 const moreThanPolkadexHas = 2n * polkadexBalance;969970 const targetAccount = helper.arrange.createEmptyAccount();971972 const uniqueMultilocation = {973 V2: {974 parents: 1,975 interior: {976 X1: {Parachain: UNIQUE_CHAIN},977 },978 },979 };980981 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(982 targetAccount.addressRaw,983 {984 Concrete: {985 parents: 0,986 interior: 'Here',987 },988 },989 moreThanPolkadexHas,990 );991992 let maliciousXcmProgramSent: any;993994995 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {996 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);997998 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);999 });10001001 await expectFailedToTransact(helper, maliciousXcmProgramSent);10021003 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1004 expect(targetAccountBalance).to.be.equal(0n);1005 });10061007 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {1008 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1009 const targetAccount = helper.arrange.createEmptyAccount();10101011 const uniqueMultilocation = {1012 V2: {1013 parents: 1,1014 interior: {1015 X1: {1016 Parachain: UNIQUE_CHAIN,1017 },1018 },1019 },1020 };10211022 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1023 targetAccount.addressRaw,1024 {1025 Concrete: {1026 parents: 1,1027 interior: {1028 X1: {1029 Parachain: UNIQUE_CHAIN,1030 },1031 },1032 },1033 },1034 testAmount,1035 );10361037 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1038 targetAccount.addressRaw,1039 {1040 Concrete: {1041 parents: 0,1042 interior: 'Here',1043 },1044 },1045 testAmount,1046 );10471048 let maliciousXcmProgramFullIdSent: any;1049 let maliciousXcmProgramHereIdSent: any;1050 const maxWaitBlocks = 3;10511052 1053 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1054 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10551056 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1057 });10581059 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10601061 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1062 expect(accountBalance).to.be.equal(0n);10631064 1065 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1066 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10671068 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1069 });10701071 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10721073 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1074 expect(accountBalance).to.be.equal(0n);1075 });1076});1077107810791080describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1081 let alice: IKeyringPair;1082 let alith: IKeyringPair;10831084 const testAmount = 100_000_000_000n;1085 let uniqueParachainJunction;1086 let uniqueAccountJunction;10871088 let uniqueParachainMultilocation: any;1089 let uniqueAccountMultilocation: any;1090 let uniqueCombinedMultilocation: any;1091 let uniqueCombinedMultilocationAcala: any; 10921093 let messageSent: any;10941095 const maxWaitBlocks = 3;10961097 before(async () => {1098 await usingPlaygrounds(async (helper, privateKey) => {1099 alice = await privateKey('//Alice');11001101 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1102 uniqueAccountJunction = {1103 AccountId32: {1104 network: 'Any',1105 id: alice.addressRaw,1106 },1107 };11081109 uniqueParachainMultilocation = {1110 V2: {1111 parents: 1,1112 interior: {1113 X1: uniqueParachainJunction,1114 },1115 },1116 };11171118 uniqueAccountMultilocation = {1119 V2: {1120 parents: 0,1121 interior: {1122 X1: uniqueAccountJunction,1123 },1124 },1125 };11261127 uniqueCombinedMultilocation = {1128 V2: {1129 parents: 1,1130 interior: {1131 X2: [uniqueParachainJunction, uniqueAccountJunction],1132 },1133 },1134 };11351136 uniqueCombinedMultilocationAcala = {1137 V2: {1138 parents: 1,1139 interior: {1140 X2: [uniqueParachainJunction, uniqueAccountJunction],1141 },1142 },1143 };11441145 1146 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1147 });11481149 1150 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1151 alith = helper.account.alithAccount();1152 });1153 });1154115511561157 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1158 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1159 const id = {1160 Token: 'ACA',1161 };1162 const destination = uniqueCombinedMultilocationAcala;1163 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11641165 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1166 });11671168 await expectFailedToTransact(helper, messageSent);1169 });11701171 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1172 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1173 const id = 'SelfReserve';1174 const destination = uniqueCombinedMultilocation;1175 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11761177 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1178 });11791180 await expectFailedToTransact(helper, messageSent);1181 });11821183 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1184 await usingAstarPlaygrounds(astarUrl, async (helper) => {1185 const destinationParachain = uniqueParachainMultilocation;1186 const beneficiary = uniqueAccountMultilocation;1187 const assets = {1188 V2: [{1189 id: {1190 Concrete: {1191 parents: 0,1192 interior: 'Here',1193 },1194 },1195 fun: {1196 Fungible: testAmount,1197 },1198 }],1199 };1200 const feeAssetItem = 0;12011202 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1203 destinationParachain,1204 beneficiary,1205 assets,1206 feeAssetItem,1207 ]);12081209 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1210 });12111212 await expectFailedToTransact(helper, messageSent);1213 });12141215 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12161217 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1218 helper.arrange.createEmptyAccount().addressRaw,1219 {1220 Concrete: {1221 parents: 1,1222 interior: {1223 X1: {1224 Parachain: POLKADEX_CHAIN,1225 },1226 },1227 },1228 },1229 testAmount,1230 );12311232 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1233 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1234 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1235 });12361237 await expectFailedToTransact(helper, messageSent);1238 });1239});12401241describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1242 1243 let alice: IKeyringPair;1244 let uniqueAssetLocation;12451246 let randomAccountUnique: IKeyringPair;1247 let randomAccountMoonbeam: IKeyringPair;12481249 1250 let assetId: string;12511252 const uniqueAssetMetadata = {1253 name: 'xcUnique',1254 symbol: 'xcUNQ',1255 decimals: 18,1256 isFrozen: false,1257 minimalBalance: 1n,1258 };12591260 let balanceUniqueTokenInit: bigint;1261 let balanceUniqueTokenMiddle: bigint;1262 let balanceUniqueTokenFinal: bigint;1263 let balanceForeignUnqTokenInit: bigint;1264 let balanceForeignUnqTokenMiddle: bigint;1265 let balanceForeignUnqTokenFinal: bigint;1266 let balanceGlmrTokenInit: bigint;1267 let balanceGlmrTokenMiddle: bigint;1268 let balanceGlmrTokenFinal: bigint;12691270 before(async () => {1271 await usingPlaygrounds(async (helper, privateKey) => {1272 alice = await privateKey('//Alice');1273 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12741275 balanceForeignUnqTokenInit = 0n;12761277 1278 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1279 });12801281 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1282 const alithAccount = helper.account.alithAccount();1283 const baltatharAccount = helper.account.baltatharAccount();1284 const dorothyAccount = helper.account.dorothyAccount();12851286 randomAccountMoonbeam = helper.account.create();12871288 1289 console.log('Sponsoring Dorothy.......');1290 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1291 console.log('Sponsoring Dorothy.......DONE');1292 12931294 uniqueAssetLocation = {1295 XCM: {1296 parents: 1,1297 interior: {X1: {Parachain: UNIQUE_CHAIN}},1298 },1299 };1300 const existentialDeposit = 1n;1301 const isSufficient = true;1302 const unitsPerSecond = 1n;1303 const numAssetsWeightHint = 0;13041305 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1306 location: uniqueAssetLocation,1307 metadata: uniqueAssetMetadata,1308 existentialDeposit,1309 isSufficient,1310 unitsPerSecond,1311 numAssetsWeightHint,1312 });13131314 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13151316 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13171318 1319 console.log('Acquire Unique AssetId Info on Moonbeam.......');13201321 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13221323 console.log('UNQ asset ID is %s', assetId);1324 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1325 13261327 1328 console.log('Sponsoring random Account.......');1329 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1330 console.log('Sponsoring random Account.......DONE');1331 13321333 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1334 });13351336 await usingPlaygrounds(async (helper) => {1337 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1338 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1339 });1340 });13411342 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1343 const currencyId = {1344 NativeAssetId: 'Here',1345 };1346 const dest = {1347 V2: {1348 parents: 1,1349 interior: {1350 X2: [1351 {Parachain: MOONBEAM_CHAIN},1352 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1353 ],1354 },1355 },1356 };1357 const amount = TRANSFER_AMOUNT;13581359 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13601361 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1362 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13631364 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1365 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1366 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13671368 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1369 await helper.wait.newBlocks(3);13701371 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13721373 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1374 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1375 expect(glmrFees == 0n).to.be.true;13761377 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13781379 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1380 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1381 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1382 });1383 });13841385 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1386 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1387 const asset = {1388 V2: {1389 id: {1390 Concrete: {1391 parents: 1,1392 interior: {1393 X1: {Parachain: UNIQUE_CHAIN},1394 },1395 },1396 },1397 fun: {1398 Fungible: TRANSFER_AMOUNT,1399 },1400 },1401 };1402 const destination = {1403 V2: {1404 parents: 1,1405 interior: {1406 X2: [1407 {Parachain: UNIQUE_CHAIN},1408 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1409 ],1410 },1411 },1412 };14131414 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14151416 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14171418 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1419 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1420 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14211422 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14231424 expect(unqRandomAccountAsset).to.be.null;14251426 balanceForeignUnqTokenFinal = 0n;14271428 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1429 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1430 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1431 });14321433 await helper.wait.newBlocks(3);14341435 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1436 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1437 expect(actuallyDelivered > 0).to.be.true;14381439 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14401441 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1442 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1443 expect(unqFees == 0n).to.be.true;1444 });14451446 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1447 1448 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1449 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1450 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14511452 const moreThanMoonbeamHas = moonbeamBalance * 2n;14531454 let targetAccountBalance = 0n;1455 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14561457 const uniqueMultilocation = {1458 V2: {1459 parents: 1,1460 interior: {1461 X1: {Parachain: UNIQUE_CHAIN},1462 },1463 },1464 };14651466 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1467 targetAccount.addressRaw,1468 {1469 Concrete: {1470 parents: 0,1471 interior: 'Here',1472 },1473 },1474 moreThanMoonbeamHas,1475 );14761477 let maliciousXcmProgramSent: any;1478 const maxWaitBlocks = 3;14791480 1481 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1482 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14831484 1485 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1486 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14871488 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1489 });14901491 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1492 && event.outcome.isFailedToTransactAsset);14931494 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1495 expect(targetAccountBalance).to.be.equal(0n);14961497 1498 const validTransferAmount = moonbeamBalance / 2n;1499 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1500 targetAccount.addressRaw,1501 {1502 Concrete: {1503 parents: 0,1504 interior: 'Here',1505 },1506 },1507 validTransferAmount,1508 );15091510 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1511 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);15121513 1514 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1515 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1516 });15171518 await helper.wait.newBlocks(maxWaitBlocks);15191520 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1521 expect(targetAccountBalance).to.be.equal(validTransferAmount);1522 });15231524 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1525 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1526 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15271528 const uniqueMultilocation = {1529 V2: {1530 parents: 1,1531 interior: {1532 X1: {1533 Parachain: UNIQUE_CHAIN,1534 },1535 },1536 },1537 };15381539 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1540 targetAccount.addressRaw,1541 {1542 Concrete: {1543 parents: 0,1544 interior: {1545 X1: {1546 Parachain: UNIQUE_CHAIN,1547 },1548 },1549 },1550 },1551 testAmount,1552 );15531554 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1555 targetAccount.addressRaw,1556 {1557 Concrete: {1558 parents: 0,1559 interior: 'Here',1560 },1561 },1562 testAmount,1563 );15641565 let maliciousXcmProgramFullIdSent: any;1566 let maliciousXcmProgramHereIdSent: any;1567 const maxWaitBlocks = 3;15681569 1570 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1571 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15721573 1574 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1575 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15761577 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1578 });15791580 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1581 && event.outcome.isUntrustedReserveLocation);15821583 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1584 expect(accountBalance).to.be.equal(0n);15851586 1587 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1588 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15891590 1591 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1592 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15931594 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1595 });15961597 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1598 && event.outcome.isUntrustedReserveLocation);15991600 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1601 expect(accountBalance).to.be.equal(0n);1602 });1603});16041605describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1606 let alice: IKeyringPair;1607 let randomAccount: IKeyringPair;16081609 const UNQ_ASSET_ID_ON_ASTAR = 1;1610 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;16111612 1613 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); 1614 const unitsPerSecond = 228_000_000_000n; 1615 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); 1616 const unqToAstarArrived = 9_999_999_999_088_000_000n; 16171618 1619 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); 1620 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; 16211622 let balanceAfterUniqueToAstarXCM: bigint;16231624 before(async () => {1625 await usingPlaygrounds(async (helper, privateKey) => {1626 alice = await privateKey('//Alice');1627 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1628 console.log('randomAccount', randomAccount.address);16291630 1631 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1632 });16331634 await usingAstarPlaygrounds(astarUrl, async (helper) => {1635 console.log('1. Create foreign asset and metadata');1636 1637 await helper.assets.create(1638 alice,1639 UNQ_ASSET_ID_ON_ASTAR,1640 alice.address,1641 UNQ_MINIMAL_BALANCE_ON_ASTAR,1642 );16431644 await helper.assets.setMetadata(1645 alice,1646 UNQ_ASSET_ID_ON_ASTAR,1647 'Cross chain UNQ',1648 'xcUNQ',1649 Number(UNQ_DECIMALS),1650 );16511652 console.log('2. Register asset location on Astar');1653 const assetLocation = {1654 V2: {1655 parents: 1,1656 interior: {1657 X1: {1658 Parachain: UNIQUE_CHAIN,1659 },1660 },1661 },1662 };16631664 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16651666 console.log('3. Set UNQ payment for XCM execution on Astar');1667 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);16681669 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1670 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1671 });1672 });16731674 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1675 const destination = {1676 V2: {1677 parents: 1,1678 interior: {1679 X1: {1680 Parachain: ASTAR_CHAIN,1681 },1682 },1683 },1684 };16851686 const beneficiary = {1687 V2: {1688 parents: 0,1689 interior: {1690 X1: {1691 AccountId32: {1692 network: 'Any',1693 id: randomAccount.addressRaw,1694 },1695 },1696 },1697 },1698 };16991700 const assets = {1701 V2: [1702 {1703 id: {1704 Concrete: {1705 parents: 0,1706 interior: 'Here',1707 },1708 },1709 fun: {1710 Fungible: unqToAstarTransferred,1711 },1712 },1713 ],1714 };17151716 1717 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1718 console.log(`Initial balance is: ${balanceBefore}`);17191720 const feeAssetItem = 0;1721 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');17221723 1724 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1725 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1726 console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1727 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17281729 await usingAstarPlaygrounds(astarUrl, async (helper) => {1730 await helper.wait.newBlocks(3);1731 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1732 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17331734 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1735 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);17361737 expect(xcUNQbalance).to.eq(unqToAstarArrived);1738 1739 expect(astarBalance).to.eq(astarInitialBalance);1740 });1741 });17421743 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1744 await usingAstarPlaygrounds(astarUrl, async (helper) => {1745 const destination = {1746 V2: {1747 parents: 1,1748 interior: {1749 X1: {1750 Parachain: UNIQUE_CHAIN,1751 },1752 },1753 },1754 };17551756 const beneficiary = {1757 V2: {1758 parents: 0,1759 interior: {1760 X1: {1761 AccountId32: {1762 network: 'Any',1763 id: randomAccount.addressRaw,1764 },1765 },1766 },1767 },1768 };17691770 const assets = {1771 V2: [1772 {1773 id: {1774 Concrete: {1775 parents: 1,1776 interior: {1777 X1: {1778 Parachain: UNIQUE_CHAIN,1779 },1780 },1781 },1782 },1783 fun: {1784 Fungible: unqFromAstarTransfered,1785 },1786 },1787 ],1788 };17891790 1791 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1792 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1793 expect(balanceASTRbefore).to.eq(astarInitialBalance);17941795 const feeAssetItem = 0;1796 1797 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17981799 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1800 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1801 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);18021803 1804 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1805 1806 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1807 });18081809 await helper.wait.newBlocks(3);1810 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1811 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1812 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1813 });18141815 itSub('Astar can send only up to its balance', async ({helper}) => {1816 1817 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1818 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1819 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);18201821 const moreThanAstarHas = astarBalance * 2n;18221823 let targetAccountBalance = 0n;1824 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18251826 const uniqueMultilocation = {1827 V2: {1828 parents: 1,1829 interior: {1830 X1: {Parachain: UNIQUE_CHAIN},1831 },1832 },1833 };18341835 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1836 targetAccount.addressRaw,1837 {1838 Concrete: {1839 parents: 0,1840 interior: 'Here',1841 },1842 },1843 moreThanAstarHas,1844 );18451846 let maliciousXcmProgramSent: any;1847 const maxWaitBlocks = 3;18481849 1850 await usingAstarPlaygrounds(astarUrl, async (helper) => {1851 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18521853 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1854 });18551856 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1857 && event.outcome.isFailedToTransactAsset);18581859 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1860 expect(targetAccountBalance).to.be.equal(0n);18611862 1863 const validTransferAmount = astarBalance / 2n;1864 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1865 targetAccount.addressRaw,1866 {1867 Concrete: {1868 parents: 0,1869 interior: 'Here',1870 },1871 },1872 validTransferAmount,1873 );18741875 await usingAstarPlaygrounds(astarUrl, async (helper) => {1876 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1877 });18781879 await helper.wait.newBlocks(maxWaitBlocks);18801881 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1882 expect(targetAccountBalance).to.be.equal(validTransferAmount);1883 });18841885 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1886 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1887 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18881889 const uniqueMultilocation = {1890 V2: {1891 parents: 1,1892 interior: {1893 X1: {1894 Parachain: UNIQUE_CHAIN,1895 },1896 },1897 },1898 };18991900 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1901 targetAccount.addressRaw,1902 {1903 Concrete: {1904 parents: 1,1905 interior: {1906 X1: {1907 Parachain: UNIQUE_CHAIN,1908 },1909 },1910 },1911 },1912 testAmount,1913 );19141915 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1916 targetAccount.addressRaw,1917 {1918 Concrete: {1919 parents: 0,1920 interior: 'Here',1921 },1922 },1923 testAmount,1924 );19251926 let maliciousXcmProgramFullIdSent: any;1927 let maliciousXcmProgramHereIdSent: any;1928 const maxWaitBlocks = 3;19291930 1931 await usingAstarPlaygrounds(astarUrl, async (helper) => {1932 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);19331934 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1935 });19361937 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1938 && event.outcome.isUntrustedReserveLocation);19391940 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1941 expect(accountBalance).to.be.equal(0n);19421943 1944 await usingAstarPlaygrounds(astarUrl, async (helper) => {1945 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19461947 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1948 });19491950 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1951 && event.outcome.isUntrustedReserveLocation);19521953 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1954 expect(accountBalance).to.be.equal(0n);1955 });1956});