1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import config from '../config';19import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds, usingShidenPlaygrounds} from '../util';20import {DevUniqueHelper, Event} from '../util/playgrounds/unique.dev';2122const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095);23const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000);24const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000);25const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023);26const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007);2728const STATEMINE_PALLET_INSTANCE = 50;2930const relayUrl = config.relayUrl;31const statemineUrl = config.statemineUrl;32const karuraUrl = config.karuraUrl;33const moonriverUrl = config.moonriverUrl;34const shidenUrl = config.shidenUrl;3536const RELAY_DECIMALS = 12;37const STATEMINE_DECIMALS = 12;38const KARURA_DECIMALS = 12;39const SHIDEN_DECIMALS = 18n;40const QTZ_DECIMALS = 18n;4142const TRANSFER_AMOUNT = 2000000000000000000000000n;4344const FUNDING_AMOUNT = 3_500_000_0000_000_000n;4546const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;4748const USDT_ASSET_ID = 100;49const USDT_ASSET_METADATA_DECIMALS = 18;50const USDT_ASSET_METADATA_NAME = 'USDT';51const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';52const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;53const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;5455const SAFE_XCM_VERSION = 2;5657describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {58 let alice: IKeyringPair;59 let bob: IKeyringPair;6061 let balanceStmnBefore: bigint;62 let balanceStmnAfter: bigint;6364 let balanceQuartzBefore: bigint;65 let balanceQuartzAfter: bigint;66 let balanceQuartzFinal: bigint;6768 let balanceBobBefore: bigint;69 let balanceBobAfter: bigint;70 let balanceBobFinal: bigint;7172 let balanceBobRelayTokenBefore: bigint;73 let balanceBobRelayTokenAfter: bigint;747576 before(async () => {77 await usingPlaygrounds(async (helper, privateKey) => {78 alice = await privateKey('//Alice');79 bob = await privateKey('//Bob'); 8081 82 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);83 });8485 await usingRelayPlaygrounds(relayUrl, async (helper) => {86 87 await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);88 await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);89 });9091 await usingStateminePlaygrounds(statemineUrl, async (helper) => {92 const sovereignFundingAmount = 3_500_000_000n;9394 await helper.assets.create(95 alice,96 USDT_ASSET_ID,97 alice.address,98 USDT_ASSET_METADATA_MINIMAL_BALANCE,99 );100 await helper.assets.setMetadata(101 alice,102 USDT_ASSET_ID,103 USDT_ASSET_METADATA_NAME,104 USDT_ASSET_METADATA_DESCRIPTION,105 USDT_ASSET_METADATA_DECIMALS,106 );107 await helper.assets.mint(108 alice,109 USDT_ASSET_ID,110 alice.address,111 USDT_ASSET_AMOUNT,112 );113114 115 116 117 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);118 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);119 });120121122 await usingPlaygrounds(async (helper) => {123 const location = {124 V2: {125 parents: 1,126 interior: {X3: [127 {128 Parachain: STATEMINE_CHAIN,129 },130 {131 PalletInstance: STATEMINE_PALLET_INSTANCE,132 },133 {134 GeneralIndex: USDT_ASSET_ID,135 },136 ]},137 },138 };139140 const metadata =141 {142 name: USDT_ASSET_ID,143 symbol: USDT_ASSET_METADATA_NAME,144 decimals: USDT_ASSET_METADATA_DECIMALS,145 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,146 };147 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);148 balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);149 });150151152 153 154 await usingRelayPlaygrounds(relayUrl, async (helper) => {155 const destination = {156 V2: {157 parents: 0,158 interior: {X1: {159 Parachain: QUARTZ_CHAIN,160 },161 },162 }};163164 const beneficiary = {165 V2: {166 parents: 0,167 interior: {X1: {168 AccountId32: {169 network: 'Any',170 id: alice.addressRaw,171 },172 }},173 },174 };175176 const assets = {177 V2: [178 {179 id: {180 Concrete: {181 parents: 0,182 interior: 'Here',183 },184 },185 fun: {186 Fungible: TRANSFER_AMOUNT_RELAY,187 },188 },189 ],190 };191192 const feeAssetItem = 0;193194 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');195 });196197 });198199 itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {200 await usingStateminePlaygrounds(statemineUrl, async (helper) => {201 const dest = {202 V2: {203 parents: 1,204 interior: {X1: {205 Parachain: QUARTZ_CHAIN,206 },207 },208 }};209210 const beneficiary = {211 V2: {212 parents: 0,213 interior: {X1: {214 AccountId32: {215 network: 'Any',216 id: alice.addressRaw,217 },218 }},219 },220 };221222 const assets = {223 V2: [224 {225 id: {226 Concrete: {227 parents: 0,228 interior: {229 X2: [230 {231 PalletInstance: STATEMINE_PALLET_INSTANCE,232 },233 {234 GeneralIndex: USDT_ASSET_ID,235 },236 ]},237 },238 },239 fun: {240 Fungible: TRANSFER_AMOUNT,241 },242 },243 ],244 };245246 const feeAssetItem = 0;247248 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);249 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');250251 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);252253 254 console.log(255 '[Statemine -> Quartz] transaction fees on Statemine: %s WND',256 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),257 );258 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;259260 });261262263 264 await helper.wait.newBlocks(3);265266 267 const free = await helper.ft.getBalance(1, {Substrate: alice.address});268269 balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);270271 console.log(272 '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',273 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),274 );275 console.log(276 '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',277 helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),278 );279 280 expect(free).to.be.equal(TRANSFER_AMOUNT);281 282 expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;283 });284285 itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {286 const destination = {287 V2: {288 parents: 1,289 interior: {X2: [290 {291 Parachain: STATEMINE_CHAIN,292 },293 {294 AccountId32: {295 network: 'Any',296 id: alice.addressRaw,297 },298 },299 ]},300 },301 };302303 const relayFee = 400_000_000_000_000n;304 const currencies: [any, bigint][] = [305 [306 {307 ForeignAssetId: 0,308 },309 TRANSFER_AMOUNT,310 ],311 [312 {313 NativeAssetId: 'Parent',314 },315 relayFee,316 ],317 ];318319 const feeItem = 1;320321 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');322323 324 balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);325 console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzFinal));326 expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;327328 await usingStateminePlaygrounds(statemineUrl, async (helper) => {329 await helper.wait.newBlocks(3);330331 332 333 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;334 });335 });336337 itSub('Should connect and send Relay token to Quartz', async ({helper}) => {338 balanceBobBefore = await helper.balance.getSubstrate(bob.address);339 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});340341 await usingRelayPlaygrounds(relayUrl, async (helper) => {342 const destination = {343 V2: {344 parents: 0,345 interior: {X1: {346 Parachain: QUARTZ_CHAIN,347 },348 },349 }};350351 const beneficiary = {352 V2: {353 parents: 0,354 interior: {X1: {355 AccountId32: {356 network: 'Any',357 id: bob.addressRaw,358 },359 }},360 },361 };362363 const assets = {364 V2: [365 {366 id: {367 Concrete: {368 parents: 0,369 interior: 'Here',370 },371 },372 fun: {373 Fungible: TRANSFER_AMOUNT_RELAY,374 },375 },376 ],377 };378379 const feeAssetItem = 0;380381 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');382 });383384 await helper.wait.newBlocks(3);385386 balanceBobAfter = await helper.balance.getSubstrate(bob.address);387 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});388389 const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;390 const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;391 console.log(392 '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',393 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),394 );395 console.log(396 '[Relay (Westend) -> Quartz] transaction fees: %s WND',397 helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),398 );399 console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);400 expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;401 expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;402 });403404 itSub('Should connect and send Relay token back', async ({helper}) => {405 let relayTokenBalanceBefore: bigint;406 let relayTokenBalanceAfter: bigint;407 await usingRelayPlaygrounds(relayUrl, async (helper) => {408 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);409 });410411 const destination = {412 V2: {413 parents: 1,414 interior: {415 X1:{416 AccountId32: {417 network: 'Any',418 id: bob.addressRaw,419 },420 },421 },422 },423 };424425 const currencies: any = [426 [427 {428 NativeAssetId: 'Parent',429 },430 TRANSFER_AMOUNT_RELAY,431 ],432 ];433434 const feeItem = 0;435436 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');437438 balanceBobFinal = await helper.balance.getSubstrate(bob.address);439 console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));440441 await usingRelayPlaygrounds(relayUrl, async (helper) => {442 await helper.wait.newBlocks(10);443 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);444445 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;446 console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));447 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;448 });449 });450});451452describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {453 let alice: IKeyringPair;454 let randomAccount: IKeyringPair;455456 let balanceQuartzTokenInit: bigint;457 let balanceQuartzTokenMiddle: bigint;458 let balanceQuartzTokenFinal: bigint;459 let balanceKaruraTokenInit: bigint;460 let balanceKaruraTokenMiddle: bigint;461 let balanceKaruraTokenFinal: bigint;462 let balanceQuartzForeignTokenInit: bigint;463 let balanceQuartzForeignTokenMiddle: bigint;464 let balanceQuartzForeignTokenFinal: bigint;465466 467 468 469 const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;470471 const KARURA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedKaruraIncomeFee;472473 before(async () => {474 await usingPlaygrounds(async (helper, privateKey) => {475 alice = await privateKey('//Alice');476 [randomAccount] = await helper.arrange.createAccounts([0n], alice);477478 479 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);480 });481482 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {483 const destination = {484 V1: {485 parents: 1,486 interior: {487 X1: {488 Parachain: QUARTZ_CHAIN,489 },490 },491 },492 };493494 const metadata = {495 name: 'Quartz',496 symbol: 'QTZ',497 decimals: 18,498 minimalBalance: 1000000000000000000n,499 };500501 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);502 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);503 balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);504 balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});505 });506507 await usingPlaygrounds(async (helper) => {508 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);509 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);510 });511 });512513 itSub('Should connect and send QTZ to Karura', async ({helper}) => {514 const destination = {515 V2: {516 parents: 1,517 interior: {518 X1: {519 Parachain: KARURA_CHAIN,520 },521 },522 },523 };524525 const beneficiary = {526 V2: {527 parents: 0,528 interior: {529 X1: {530 AccountId32: {531 network: 'Any',532 id: randomAccount.addressRaw,533 },534 },535 },536 },537 };538539 const assets = {540 V2: [541 {542 id: {543 Concrete: {544 parents: 0,545 interior: 'Here',546 },547 },548 fun: {549 Fungible: TRANSFER_AMOUNT,550 },551 },552 ],553 };554555 const feeAssetItem = 0;556557 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');558 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);559560 const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;561 expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;562 console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));563564 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {565 await helper.wait.newBlocks(3);566567 balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});568 balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);569570 const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;571 const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;572 const karUnqFees = TRANSFER_AMOUNT - qtzIncomeTransfer;573574 console.log(575 '[Quartz -> Karura] transaction fees on Karura: %s KAR',576 helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),577 );578 console.log(579 '[Quartz -> Karura] transaction fees on Karura: %s QTZ',580 helper.util.bigIntToDecimals(karUnqFees),581 );582 console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));583 expect(karFees == 0n).to.be.true;584 expect(585 karUnqFees == expectedKaruraIncomeFee,586 'Karura took different income fee, check the Karura foreign asset config',587 ).to.be.true;588 });589 });590591 itSub('Should connect to Karura and send QTZ back', async ({helper}) => {592 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {593 const destination = {594 V1: {595 parents: 1,596 interior: {597 X2: [598 {Parachain: QUARTZ_CHAIN},599 {600 AccountId32: {601 network: 'Any',602 id: randomAccount.addressRaw,603 },604 },605 ],606 },607 },608 };609610 const id = {611 ForeignAsset: 0,612 };613614 await helper.xTokens.transfer(randomAccount, id, KARURA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');615 balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);616 balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);617618 const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;619 const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;620621 console.log(622 '[Karura -> Quartz] transaction fees on Karura: %s KAR',623 helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),624 );625 console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));626627 expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;628 expect(qtzOutcomeTransfer == KARURA_BACKWARD_TRANSFER_AMOUNT).to.be.true;629 });630631 await helper.wait.newBlocks(3);632633 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);634 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;635 expect(actuallyDelivered > 0).to.be.true;636637 console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));638639 const qtzFees = KARURA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;640 console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));641 expect(qtzFees == 0n).to.be.true;642 });643644 itSub('Karura can send only up to its balance', async ({helper}) => {645 646 const karuraBalance = 10000n * (10n ** QTZ_DECIMALS);647 const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN);648 await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance);649650 const moreThanKaruraHas = karuraBalance * 2n;651652 let targetAccountBalance = 0n;653 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);654655 const quartzMultilocation = {656 V1: {657 parents: 1,658 interior: {659 X1: {Parachain: QUARTZ_CHAIN},660 },661 },662 };663664 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(665 targetAccount.addressRaw,666 {667 Concrete: {668 parents: 0,669 interior: 'Here',670 },671 },672 moreThanKaruraHas,673 );674675 let maliciousXcmProgramSent: any;676 const maxWaitBlocks = 3;677678 679 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {680 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);681682 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);683 });684685 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash686 && event.outcome().isFailedToTransactAsset);687688 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);689 expect(targetAccountBalance).to.be.equal(0n);690691 692 const validTransferAmount = karuraBalance / 2n;693 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(694 targetAccount.addressRaw,695 {696 Concrete: {697 parents: 0,698 interior: 'Here',699 },700 },701 validTransferAmount,702 );703704 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {705 await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);706 });707708 await helper.wait.newBlocks(maxWaitBlocks);709710 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);711 expect(targetAccountBalance).to.be.equal(validTransferAmount);712 });713714 itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => {715 const testAmount = 10_000n * (10n ** QTZ_DECIMALS);716 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);717718 const quartzMultilocation = {719 V1: {720 parents: 1,721 interior: {722 X1: {723 Parachain: QUARTZ_CHAIN,724 },725 },726 },727 };728729 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(730 targetAccount.addressRaw,731 {732 Concrete: {733 parents: 1,734 interior: {735 X1: {736 Parachain: QUARTZ_CHAIN,737 },738 },739 },740 },741 testAmount,742 );743744 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(745 targetAccount.addressRaw,746 {747 Concrete: {748 parents: 0,749 interior: 'Here',750 },751 },752 testAmount,753 );754755 let maliciousXcmProgramFullIdSent: any;756 let maliciousXcmProgramHereIdSent: any;757 const maxWaitBlocks = 3;758759 760 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {761 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);762763 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);764 });765766 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash767 && event.outcome().isUntrustedReserveLocation);768769 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);770 expect(accountBalance).to.be.equal(0n);771772 773 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {774 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);775776 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);777 });778779 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash780 && event.outcome().isUntrustedReserveLocation);781782 accountBalance = await helper.balance.getSubstrate(targetAccount.address);783 expect(accountBalance).to.be.equal(0n);784 });785});786787788789describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {790 let alice: IKeyringPair;791 let alith: IKeyringPair;792793 const testAmount = 100_000_000_000n;794 let quartzParachainJunction;795 let quartzAccountJunction;796797 let quartzParachainMultilocation: any;798 let quartzAccountMultilocation: any;799 let quartzCombinedMultilocation: any;800 let quartzCombinedMultilocationKarura: any; 801802 let messageSent: any;803804 const maxWaitBlocks = 3;805806 before(async () => {807 await usingPlaygrounds(async (helper, privateKey) => {808 alice = await privateKey('//Alice');809810 quartzParachainJunction = {Parachain: QUARTZ_CHAIN};811 quartzAccountJunction = {812 AccountId32: {813 network: 'Any',814 id: alice.addressRaw,815 },816 };817818 quartzParachainMultilocation = {819 V2: {820 parents: 1,821 interior: {822 X1: quartzParachainJunction,823 },824 },825 };826827 quartzAccountMultilocation = {828 V2: {829 parents: 0,830 interior: {831 X1: quartzAccountJunction,832 },833 },834 };835836 quartzCombinedMultilocation = {837 V2: {838 parents: 1,839 interior: {840 X2: [quartzParachainJunction, quartzAccountJunction],841 },842 },843 };844845 quartzCombinedMultilocationKarura = {846 V1: {847 parents: 1,848 interior: {849 X2: [quartzParachainJunction, quartzAccountJunction],850 },851 },852 };853854 855 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);856 });857858 859 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {860 alith = helper.account.alithAccount();861 });862 });863864 const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {865 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash866 && event.outcome().isFailedToTransactAsset);867 };868869 itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {870 await usingKaruraPlaygrounds(karuraUrl, async (helper) => {871 const id = {872 Token: 'KAR',873 };874 const destination = quartzCombinedMultilocationKarura;875 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');876877 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);878 });879880 await expectFailedToTransact(helper, messageSent);881 });882883 itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => {884 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {885 const id = 'SelfReserve';886 const destination = quartzCombinedMultilocation;887 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');888889 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);890 });891892 await expectFailedToTransact(helper, messageSent);893 });894895 itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => {896 await usingShidenPlaygrounds(shidenUrl, async (helper) => {897 const destinationParachain = quartzParachainMultilocation;898 const beneficiary = quartzAccountMultilocation;899 const assets = {900 V2: [{901 id: {902 Concrete: {903 parents: 0,904 interior: 'Here',905 },906 },907 fun: {908 Fungible: testAmount,909 },910 }],911 };912 const feeAssetItem = 0;913914 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [915 destinationParachain,916 beneficiary,917 assets,918 feeAssetItem,919 ]);920921 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);922 });923924 await expectFailedToTransact(helper, messageSent);925 });926});927928describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {929 930 let alice: IKeyringPair;931 let quartzAssetLocation;932933 let randomAccountQuartz: IKeyringPair;934 let randomAccountMoonriver: IKeyringPair;935936 937 let assetId: string;938939 const quartzAssetMetadata = {940 name: 'xcQuartz',941 symbol: 'xcQTZ',942 decimals: 18,943 isFrozen: false,944 minimalBalance: 1n,945 };946947 let balanceQuartzTokenInit: bigint;948 let balanceQuartzTokenMiddle: bigint;949 let balanceQuartzTokenFinal: bigint;950 let balanceForeignQtzTokenInit: bigint;951 let balanceForeignQtzTokenMiddle: bigint;952 let balanceForeignQtzTokenFinal: bigint;953 let balanceMovrTokenInit: bigint;954 let balanceMovrTokenMiddle: bigint;955 let balanceMovrTokenFinal: bigint;956957 before(async () => {958 await usingPlaygrounds(async (helper, privateKey) => {959 alice = await privateKey('//Alice');960 [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);961962 balanceForeignQtzTokenInit = 0n;963964 965 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);966 });967968 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {969 const alithAccount = helper.account.alithAccount();970 const baltatharAccount = helper.account.baltatharAccount();971 const dorothyAccount = helper.account.dorothyAccount();972973 randomAccountMoonriver = helper.account.create();974975 976 console.log('Sponsoring Dorothy.......');977 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);978 console.log('Sponsoring Dorothy.......DONE');979 980981 quartzAssetLocation = {982 XCM: {983 parents: 1,984 interior: {X1: {Parachain: QUARTZ_CHAIN}},985 },986 };987 const existentialDeposit = 1n;988 const isSufficient = true;989 const unitsPerSecond = 1n;990 const numAssetsWeightHint = 0;991992 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({993 location: quartzAssetLocation,994 metadata: quartzAssetMetadata,995 existentialDeposit,996 isSufficient,997 unitsPerSecond,998 numAssetsWeightHint,999 });10001001 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);10021003 await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);10041005 1006 console.log('Acquire Quartz AssetId Info on Moonriver.......');10071008 assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();10091010 console.log('QTZ asset ID is %s', assetId);1011 console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');1012 10131014 1015 console.log('Sponsoring random Account.......');1016 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);1017 console.log('Sponsoring random Account.......DONE');1018 10191020 balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);1021 });10221023 await usingPlaygrounds(async (helper) => {1024 await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);1025 balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);1026 });1027 });10281029 itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {1030 const currencyId = {1031 NativeAssetId: 'Here',1032 };1033 const dest = {1034 V2: {1035 parents: 1,1036 interior: {1037 X2: [1038 {Parachain: MOONRIVER_CHAIN},1039 {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},1040 ],1041 },1042 },1043 };1044 const amount = TRANSFER_AMOUNT;10451046 await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');10471048 balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);1049 expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;10501051 const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;1052 console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));1053 expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;10541055 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {1056 await helper.wait.newBlocks(3);10571058 balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);10591060 const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;1061 console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));1062 expect(movrFees == 0n).to.be.true;10631064 balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; 1065 const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;1066 console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));1067 expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1068 });1069 });10701071 itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {1072 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {1073 const asset = {1074 V2: {1075 id: {1076 Concrete: {1077 parents: 1,1078 interior: {1079 X1: {Parachain: QUARTZ_CHAIN},1080 },1081 },1082 },1083 fun: {1084 Fungible: TRANSFER_AMOUNT,1085 },1086 },1087 };1088 const destination = {1089 V2: {1090 parents: 1,1091 interior: {1092 X2: [1093 {Parachain: QUARTZ_CHAIN},1094 {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},1095 ],1096 },1097 },1098 };10991100 await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');11011102 balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);11031104 const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;1105 console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));1106 expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;11071108 const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);11091110 expect(qtzRandomAccountAsset).to.be.null;11111112 balanceForeignQtzTokenFinal = 0n;11131114 const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;1115 console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));1116 expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1117 });11181119 await helper.wait.newBlocks(3);11201121 balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);1122 const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;1123 expect(actuallyDelivered > 0).to.be.true;11241125 console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));11261127 const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;1128 console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));1129 expect(qtzFees == 0n).to.be.true;1130 });11311132 itSub('Moonriver can send only up to its balance', async ({helper}) => {1133 1134 const moonriverBalance = 10000n * (10n ** QTZ_DECIMALS);1135 const moonriverSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONRIVER_CHAIN);1136 await helper.getSudo().balance.setBalanceSubstrate(alice, moonriverSovereignAccount, moonriverBalance);11371138 const moreThanMoonriverHas = moonriverBalance * 2n;11391140 let targetAccountBalance = 0n;1141 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);11421143 const quartzMultilocation = {1144 V2: {1145 parents: 1,1146 interior: {1147 X1: {Parachain: QUARTZ_CHAIN},1148 },1149 },1150 };11511152 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1153 targetAccount.addressRaw,1154 {1155 Concrete: {1156 parents: 0,1157 interior: 'Here',1158 },1159 },1160 moreThanMoonriverHas,1161 );11621163 let maliciousXcmProgramSent: any;1164 const maxWaitBlocks = 3;11651166 1167 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {1168 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgram]);11691170 1171 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1172 await helper.fastDemocracy.executeProposal('try to spend more QTZ than Moonriver has', batchCall);11731174 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1175 });11761177 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1178 && event.outcome().isFailedToTransactAsset);11791180 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1181 expect(targetAccountBalance).to.be.equal(0n);11821183 1184 const validTransferAmount = moonriverBalance / 2n;1185 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1186 targetAccount.addressRaw,1187 {1188 Concrete: {1189 parents: 0,1190 interior: 'Here',1191 },1192 },1193 validTransferAmount,1194 );11951196 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {1197 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, validXcmProgram]);11981199 1200 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1201 await helper.fastDemocracy.executeProposal('Spend the correct amount of QTZ', batchCall);1202 });12031204 await helper.wait.newBlocks(maxWaitBlocks);12051206 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1207 expect(targetAccountBalance).to.be.equal(validTransferAmount);1208 });12091210 itSub('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {1211 const testAmount = 10_000n * (10n ** QTZ_DECIMALS);1212 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);12131214 const quartzMultilocation = {1215 V2: {1216 parents: 1,1217 interior: {1218 X1: {1219 Parachain: QUARTZ_CHAIN,1220 },1221 },1222 },1223 };12241225 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1226 targetAccount.addressRaw,1227 {1228 Concrete: {1229 parents: 0,1230 interior: {1231 X1: {1232 Parachain: QUARTZ_CHAIN,1233 },1234 },1235 },1236 },1237 testAmount,1238 );12391240 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1241 targetAccount.addressRaw,1242 {1243 Concrete: {1244 parents: 0,1245 interior: 'Here',1246 },1247 },1248 testAmount,1249 );12501251 let maliciousXcmProgramFullIdSent: any;1252 let maliciousXcmProgramHereIdSent: any;1253 const maxWaitBlocks = 3;12541255 1256 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {1257 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramFullId]);12581259 1260 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1261 await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using path asset identification', batchCall);12621263 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1264 });12651266 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1267 && event.outcome().isUntrustedReserveLocation);12681269 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1270 expect(accountBalance).to.be.equal(0n);12711272 1273 await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {1274 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramHereId]);12751276 1277 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1278 await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using "here" asset identification', batchCall);12791280 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1281 });12821283 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1284 && event.outcome().isUntrustedReserveLocation);12851286 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1287 expect(accountBalance).to.be.equal(0n);1288 });1289});12901291describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {1292 let alice: IKeyringPair;1293 let sender: IKeyringPair;12941295 const QTZ_ASSET_ID_ON_SHIDEN = 1;1296 const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n;12971298 1299 const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); 1300 const unitsPerSecond = 228_000_000_000n; 1301 const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); 1302 const qtzToShidenArrived = 9_999_999_999_088_000_000n; 13031304 1305 const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); 1306 const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; 13071308 let balanceAfterQuartzToShidenXCM: bigint;13091310 before(async () => {1311 await usingPlaygrounds(async (helper, privateKey) => {1312 alice = await privateKey('//Alice');1313 [sender] = await helper.arrange.createAccounts([100n], alice);1314 console.log('sender', sender.address);13151316 1317 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1318 });13191320 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1321 console.log('1. Create foreign asset and metadata');1322 1323 await helper.assets.create(1324 alice,1325 QTZ_ASSET_ID_ON_SHIDEN,1326 alice.address,1327 QTZ_MINIMAL_BALANCE_ON_SHIDEN,1328 );13291330 await helper.assets.setMetadata(1331 alice,1332 QTZ_ASSET_ID_ON_SHIDEN,1333 'Cross chain QTZ',1334 'xcQTZ',1335 Number(QTZ_DECIMALS),1336 );13371338 console.log('2. Register asset location on Shiden');1339 const assetLocation = {1340 V2: {1341 parents: 1,1342 interior: {1343 X1: {1344 Parachain: QUARTZ_CHAIN,1345 },1346 },1347 },1348 };13491350 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);13511352 console.log('3. Set QTZ payment for XCM execution on Shiden');1353 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);13541355 console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');1356 await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);1357 });1358 });13591360 itSub('Should connect and send QTZ to Shiden', async ({helper}) => {1361 const destination = {1362 V2: {1363 parents: 1,1364 interior: {1365 X1: {1366 Parachain: SHIDEN_CHAIN,1367 },1368 },1369 },1370 };13711372 const beneficiary = {1373 V2: {1374 parents: 0,1375 interior: {1376 X1: {1377 AccountId32: {1378 network: 'Any',1379 id: sender.addressRaw,1380 },1381 },1382 },1383 },1384 };13851386 const assets = {1387 V2: [1388 {1389 id: {1390 Concrete: {1391 parents: 0,1392 interior: 'Here',1393 },1394 },1395 fun: {1396 Fungible: qtzToShidenTransferred,1397 },1398 },1399 ],1400 };14011402 1403 const balanceBefore = await helper.balance.getSubstrate(sender.address);1404 console.log(`Initial balance is: ${balanceBefore}`);14051406 const feeAssetItem = 0;1407 await helper.xcm.limitedReserveTransferAssets(sender, destination, beneficiary, assets, feeAssetItem, 'Unlimited');14081409 1410 balanceAfterQuartzToShidenXCM = await helper.balance.getSubstrate(sender.address);1411 console.log(`QTZ Balance on Quartz after XCM is: ${balanceAfterQuartzToShidenXCM}`);1412 console.log(`Quartz's QTZ commission is: ${balanceBefore - balanceAfterQuartzToShidenXCM}`);1413 expect(balanceBefore - balanceAfterQuartzToShidenXCM > 0).to.be.true;14141415 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1416 await helper.wait.newBlocks(3);1417 const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address);1418 const shidenBalance = await helper.balance.getSubstrate(sender.address);14191420 console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);1421 console.log(`Shiden's QTZ commission is: ${qtzToShidenTransferred - xcQTZbalance!}`);14221423 expect(xcQTZbalance).to.eq(qtzToShidenArrived);1424 1425 expect(shidenBalance).to.eq(shidenInitialBalance);1426 });1427 });14281429 itSub('Should connect to Shiden and send QTZ back', async ({helper}) => {1430 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1431 const destination = {1432 V2: {1433 parents: 1,1434 interior: {1435 X1: {1436 Parachain: QUARTZ_CHAIN,1437 },1438 },1439 },1440 };14411442 const beneficiary = {1443 V2: {1444 parents: 0,1445 interior: {1446 X1: {1447 AccountId32: {1448 network: 'Any',1449 id: sender.addressRaw,1450 },1451 },1452 },1453 },1454 };14551456 const assets = {1457 V2: [1458 {1459 id: {1460 Concrete: {1461 parents: 1,1462 interior: {1463 X1: {1464 Parachain: QUARTZ_CHAIN,1465 },1466 },1467 },1468 },1469 fun: {1470 Fungible: qtzFromShidenTransfered,1471 },1472 },1473 ],1474 };14751476 1477 const balanceSDNbefore = await helper.balance.getSubstrate(sender.address);1478 console.log(`SDN balance is: ${balanceSDNbefore}, it does not changed`);1479 expect(balanceSDNbefore).to.eq(shidenInitialBalance);14801481 const feeAssetItem = 0;1482 1483 await helper.executeExtrinsic(sender, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);14841485 1486 const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address);1487 const balanceSDN = await helper.balance.getSubstrate(sender.address);1488 console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);14891490 1491 expect(xcQTZbalance).to.eq(qtzOnShidenLeft);1492 1493 expect(balanceSDN / (10n ** (SHIDEN_DECIMALS - 3n))).to.eq(996n);1494 });14951496 await helper.wait.newBlocks(3);1497 const balanceQTZ = await helper.balance.getSubstrate(sender.address);1498 console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`);1499 expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);1500 });15011502 itSub('Shiden can send only up to its balance', async ({helper}) => {1503 1504 const shidenBalance = 10000n * (10n ** QTZ_DECIMALS);1505 const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN);1506 await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance);15071508 const moreThanShidenHas = shidenBalance * 2n;15091510 let targetAccountBalance = 0n;1511 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);15121513 const quartzMultilocation = {1514 V2: {1515 parents: 1,1516 interior: {1517 X1: {Parachain: QUARTZ_CHAIN},1518 },1519 },1520 };15211522 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1523 targetAccount.addressRaw,1524 {1525 Concrete: {1526 parents: 0,1527 interior: 'Here',1528 },1529 },1530 moreThanShidenHas,1531 );15321533 let maliciousXcmProgramSent: any;1534 const maxWaitBlocks = 3;15351536 1537 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1538 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);15391540 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1541 });15421543 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1544 && event.outcome().isFailedToTransactAsset);15451546 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1547 expect(targetAccountBalance).to.be.equal(0n);15481549 1550 const validTransferAmount = shidenBalance / 2n;1551 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1552 targetAccount.addressRaw,1553 {1554 Concrete: {1555 parents: 0,1556 interior: 'Here',1557 },1558 },1559 validTransferAmount,1560 );15611562 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1563 await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);1564 });15651566 await helper.wait.newBlocks(maxWaitBlocks);15671568 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1569 expect(targetAccountBalance).to.be.equal(validTransferAmount);1570 });15711572 itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => {1573 const testAmount = 10_000n * (10n ** QTZ_DECIMALS);1574 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15751576 const quartzMultilocation = {1577 V2: {1578 parents: 1,1579 interior: {1580 X1: {1581 Parachain: QUARTZ_CHAIN,1582 },1583 },1584 },1585 };15861587 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1588 targetAccount.addressRaw,1589 {1590 Concrete: {1591 parents: 1,1592 interior: {1593 X1: {1594 Parachain: QUARTZ_CHAIN,1595 },1596 },1597 },1598 },1599 testAmount,1600 );16011602 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1603 targetAccount.addressRaw,1604 {1605 Concrete: {1606 parents: 0,1607 interior: 'Here',1608 },1609 },1610 testAmount,1611 );16121613 let maliciousXcmProgramFullIdSent: any;1614 let maliciousXcmProgramHereIdSent: any;1615 const maxWaitBlocks = 3;16161617 1618 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1619 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);16201621 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1622 });16231624 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1625 && event.outcome().isUntrustedReserveLocation);16261627 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1628 expect(accountBalance).to.be.equal(0n);16291630 1631 await usingShidenPlaygrounds(shidenUrl, async (helper) => {1632 await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);16331634 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1635 });16361637 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1638 && event.outcome().isUntrustedReserveLocation);16391640 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1641 expect(accountBalance).to.be.equal(0n);1642 });1643});