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 {hexToString, 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;6061const uniqueMultilocation = {62 V2: {63 parents: 1,64 interior: {65 X1: {66 Parachain: UNIQUE_CHAIN,67 },68 },69 },70};7172const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {73 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash74 && event.outcome.isFailedToTransactAsset);75};76const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {77 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash78 && event.outcome.isUntrustedReserveLocation);79};80describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {81 let alice: IKeyringPair;82 let bob: IKeyringPair;8384 let balanceStmnBefore: bigint;85 let balanceStmnAfter: bigint;8687 let balanceUniqueBefore: bigint;88 let balanceUniqueAfter: bigint;89 let balanceUniqueFinal: bigint;9091 let balanceBobBefore: bigint;92 let balanceBobAfter: bigint;93 let balanceBobFinal: bigint;9495 let balanceBobRelayTokenBefore: bigint;96 let balanceBobRelayTokenAfter: bigint;979899 before(async () => {100 await usingPlaygrounds(async (helper, privateKey) => {101 alice = await privateKey('//Alice');102 bob = await privateKey('//Bob'); 103104 105 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);106 });107108 await usingRelayPlaygrounds(relayUrl, async (helper) => {109 110 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);111 await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);112 });113114 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {115 const sovereignFundingAmount = 3_500_000_000n;116117 await helper.assets.create(118 alice,119 USDT_ASSET_ID,120 alice.address,121 USDT_ASSET_METADATA_MINIMAL_BALANCE,122 );123 await helper.assets.setMetadata(124 alice,125 USDT_ASSET_ID,126 USDT_ASSET_METADATA_NAME,127 USDT_ASSET_METADATA_DESCRIPTION,128 USDT_ASSET_METADATA_DECIMALS,129 );130 await helper.assets.mint(131 alice,132 USDT_ASSET_ID,133 alice.address,134 USDT_ASSET_AMOUNT,135 );136137 138 139 140 const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);141 await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);142 });143144145 await usingPlaygrounds(async (helper) => {146 const location = {147 V2: {148 parents: 1,149 interior: {X3: [150 {151 Parachain: STATEMINT_CHAIN,152 },153 {154 PalletInstance: STATEMINT_PALLET_INSTANCE,155 },156 {157 GeneralIndex: USDT_ASSET_ID,158 },159 ]},160 },161 };162163 const metadata =164 {165 name: USDT_ASSET_ID,166 symbol: USDT_ASSET_METADATA_NAME,167 decimals: USDT_ASSET_METADATA_DECIMALS,168 minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,169 };170 await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);171 balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);172 });173174175 176 177 await usingRelayPlaygrounds(relayUrl, async (helper) => {178 const destination = {179 V2: {180 parents: 0,181 interior: {X1: {182 Parachain: UNIQUE_CHAIN,183 },184 },185 }};186187 const beneficiary = {188 V2: {189 parents: 0,190 interior: {X1: {191 AccountId32: {192 network: 'Any',193 id: alice.addressRaw,194 },195 }},196 },197 };198199 const assets = {200 V2: [201 {202 id: {203 Concrete: {204 parents: 0,205 interior: 'Here',206 },207 },208 fun: {209 Fungible: TRANSFER_AMOUNT_RELAY,210 },211 },212 ],213 };214215 const feeAssetItem = 0;216217 await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');218 });219220 });221222 itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {223 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {224 const dest = {225 V2: {226 parents: 1,227 interior: {X1: {228 Parachain: UNIQUE_CHAIN,229 },230 },231 }};232233 const beneficiary = {234 V2: {235 parents: 0,236 interior: {X1: {237 AccountId32: {238 network: 'Any',239 id: alice.addressRaw,240 },241 }},242 },243 };244245 const assets = {246 V2: [247 {248 id: {249 Concrete: {250 parents: 0,251 interior: {252 X2: [253 {254 PalletInstance: STATEMINT_PALLET_INSTANCE,255 },256 {257 GeneralIndex: USDT_ASSET_ID,258 },259 ]},260 },261 },262 fun: {263 Fungible: TRANSFER_AMOUNT,264 },265 },266 ],267 };268269 const feeAssetItem = 0;270271 balanceStmnBefore = await helper.balance.getSubstrate(alice.address);272 await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');273274 balanceStmnAfter = await helper.balance.getSubstrate(alice.address);275276 277 console.log(278 '[Statemint -> Unique] transaction fees on Statemint: %s WND',279 helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),280 );281 expect(balanceStmnBefore > balanceStmnAfter).to.be.true;282283 });284285286 287 await helper.wait.newBlocks(3);288289 290 const free = await helper.ft.getBalance(1, {Substrate: alice.address});291292 balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);293294 console.log(295 '[Statemint -> Unique] transaction fees on Unique: %s USDT',296 helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),297 );298 console.log(299 '[Statemint -> Unique] transaction fees on Unique: %s UNQ',300 helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),301 );302 303 expect(free).to.be.equal(TRANSFER_AMOUNT);304 305 expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;306 });307308 itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {309 const destination = {310 V2: {311 parents: 1,312 interior: {X2: [313 {314 Parachain: STATEMINT_CHAIN,315 },316 {317 AccountId32: {318 network: 'Any',319 id: alice.addressRaw,320 },321 },322 ]},323 },324 };325326 const relayFee = 400_000_000_000_000n;327 const currencies: [any, bigint][] = [328 [329 {330 ForeignAssetId: 0,331 },332 TRANSFER_AMOUNT,333 ],334 [335 {336 NativeAssetId: 'Parent',337 },338 relayFee,339 ],340 ];341342 const feeItem = 1;343344 await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');345346 347 balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);348 console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));349 expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;350351 await usingStatemintPlaygrounds(statemintUrl, async (helper) => {352 await helper.wait.newBlocks(3);353354 355 356 expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;357 });358 });359360 itSub('Should connect and send Relay token to Unique', async ({helper}) => {361 balanceBobBefore = await helper.balance.getSubstrate(bob.address);362 balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});363364 await usingRelayPlaygrounds(relayUrl, async (helper) => {365 const destination = {366 V2: {367 parents: 0,368 interior: {X1: {369 Parachain: UNIQUE_CHAIN,370 },371 },372 }};373374 const beneficiary = {375 V2: {376 parents: 0,377 interior: {X1: {378 AccountId32: {379 network: 'Any',380 id: bob.addressRaw,381 },382 }},383 },384 };385386 const assets = {387 V2: [388 {389 id: {390 Concrete: {391 parents: 0,392 interior: 'Here',393 },394 },395 fun: {396 Fungible: TRANSFER_AMOUNT_RELAY,397 },398 },399 ],400 };401402 const feeAssetItem = 0;403404 await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');405 });406407 await helper.wait.newBlocks(3);408409 balanceBobAfter = await helper.balance.getSubstrate(bob.address);410 balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});411412 const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;413 const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;414 console.log(415 '[Relay (Westend) -> Unique] transaction fees: %s UNQ',416 helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),417 );418 console.log(419 '[Relay (Westend) -> Unique] transaction fees: %s WND',420 helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),421 );422 console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);423 expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;424 expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;425 });426427 itSub('Should connect and send Relay token back', async ({helper}) => {428 let relayTokenBalanceBefore: bigint;429 let relayTokenBalanceAfter: bigint;430 await usingRelayPlaygrounds(relayUrl, async (helper) => {431 relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);432 });433434 const destination = {435 V2: {436 parents: 1,437 interior: {438 X1:{439 AccountId32: {440 network: 'Any',441 id: bob.addressRaw,442 },443 },444 },445 },446 };447448 const currencies: any = [449 [450 {451 NativeAssetId: 'Parent',452 },453 TRANSFER_AMOUNT_RELAY,454 ],455 ];456457 const feeItem = 0;458459 await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');460461 balanceBobFinal = await helper.balance.getSubstrate(bob.address);462 console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ', helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));463464 await usingRelayPlaygrounds(relayUrl, async (helper) => {465 await helper.wait.newBlocks(10);466 relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);467468 const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;469 console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));470 expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;471 });472 });473});474475describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {476 let alice: IKeyringPair;477 let randomAccount: IKeyringPair;478479 let balanceUniqueTokenInit: bigint;480 let balanceUniqueTokenMiddle: bigint;481 let balanceUniqueTokenFinal: bigint;482 let balanceAcalaTokenInit: bigint;483 let balanceAcalaTokenMiddle: bigint;484 let balanceAcalaTokenFinal: bigint;485 let balanceUniqueForeignTokenInit: bigint;486 let balanceUniqueForeignTokenMiddle: bigint;487 let balanceUniqueForeignTokenFinal: bigint;488489 490 491 492 const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;493 const acalaEps = 8n * 10n ** 16n;494495 let acalaBackwardTransferAmount: bigint;496497 before(async () => {498 await usingPlaygrounds(async (helper, privateKey) => {499 alice = await privateKey('//Alice');500 [randomAccount] = await helper.arrange.createAccounts([0n], alice);501502 503 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);504 });505506 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {507 const destination = {508 V2: {509 parents: 1,510 interior: {511 X1: {512 Parachain: UNIQUE_CHAIN,513 },514 },515 },516 };517518 const metadata = {519 name: 'Unique Network',520 symbol: 'UNQ',521 decimals: 18,522 minimalBalance: 1250000000000000000n,523 };524 const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>525 hexToString(v.toJSON()['symbol'])) as string[];526527 if(!assets.includes('UNQ')) {528 await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);529 } else {530 console.log('UNQ token already registered on Acala assetRegistry pallet');531 }532 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);533 balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);534 balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});535 });536537 await usingPlaygrounds(async (helper) => {538 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);539 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);540 });541 });542543 itSub('Should connect and send UNQ to Acala', async ({helper}) => {544545 const destination = {546 V2: {547 parents: 1,548 interior: {549 X1: {550 Parachain: ACALA_CHAIN,551 },552 },553 },554 };555556 const beneficiary = {557 V2: {558 parents: 0,559 interior: {560 X1: {561 AccountId32: {562 network: 'Any',563 id: randomAccount.addressRaw,564 },565 },566 },567 },568 };569570 const assets = {571 V2: [572 {573 id: {574 Concrete: {575 parents: 0,576 interior: 'Here',577 },578 },579 fun: {580 Fungible: TRANSFER_AMOUNT,581 },582 },583 ],584 };585586 const feeAssetItem = 0;587588 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');589 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);590591 const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;592 console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));593 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;594595 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {596 await helper.wait.newBlocks(3);597598 balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});599 balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);600601 const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;602 const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;603 acalaBackwardTransferAmount = unqIncomeTransfer;604605 const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;606607 console.log(608 '[Unique -> Acala] transaction fees on Acala: %s ACA',609 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),610 );611 console.log(612 '[Unique -> Acala] transaction fees on Acala: %s UNQ',613 helper.util.bigIntToDecimals(acaUnqFees),614 );615 console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));616 expect(acaFees == 0n).to.be.true;617618 const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;619620 expect(621 bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,622 'Acala took different income fee, check the Acala foreign asset config',623 ).to.be.true;624 });625 });626627 itSub('Should connect to Acala and send UNQ back', async ({helper}) => {628 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {629 const destination = {630 V2: {631 parents: 1,632 interior: {633 X2: [634 {Parachain: UNIQUE_CHAIN},635 {636 AccountId32: {637 network: 'Any',638 id: randomAccount.addressRaw,639 },640 },641 ],642 },643 },644 };645646 const id = {647 ForeignAsset: 0,648 };649650 await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');651 balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);652 balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);653654 const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;655 const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;656657 console.log(658 '[Acala -> Unique] transaction fees on Acala: %s ACA',659 helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),660 );661 console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));662663 expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;664 expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;665 });666667 await helper.wait.newBlocks(3);668669 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);670 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;671 expect(actuallyDelivered > 0).to.be.true;672673 console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));674675 const unqFees = acalaBackwardTransferAmount - actuallyDelivered;676 console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));677 expect(unqFees == 0n).to.be.true;678 });679680 itSub('Acala can send only up to its balance', async ({helper}) => {681 682 const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);683 const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);684 await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);685686 const moreThanAcalaHas = acalaBalance * 2n;687688 let targetAccountBalance = 0n;689 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);690691 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(692 targetAccount.addressRaw,693 {694 Concrete: {695 parents: 0,696 interior: 'Here',697 },698 },699 moreThanAcalaHas,700 );701702 let maliciousXcmProgramSent: any;703 const maxWaitBlocks = 3;704705 706 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {707 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);708709 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);710 });711712 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash713 && event.outcome.isFailedToTransactAsset);714715 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);716 expect(targetAccountBalance).to.be.equal(0n);717718 719 const validTransferAmount = acalaBalance / 2n;720 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(721 targetAccount.addressRaw,722 {723 Concrete: {724 parents: 0,725 interior: 'Here',726 },727 },728 validTransferAmount,729 );730731 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {732 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);733 });734735 await helper.wait.newBlocks(maxWaitBlocks);736737 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);738 expect(targetAccountBalance).to.be.equal(validTransferAmount);739 });740741 itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {742 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);743 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);744745 const uniqueMultilocation = {746 V2: {747 parents: 1,748 interior: {749 X1: {750 Parachain: UNIQUE_CHAIN,751 },752 },753 },754 };755756 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(757 targetAccount.addressRaw,758 {759 Concrete: {760 parents: 1,761 interior: {762 X1: {763 Parachain: UNIQUE_CHAIN,764 },765 },766 },767 },768 testAmount,769 );770771 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(772 targetAccount.addressRaw,773 {774 Concrete: {775 parents: 0,776 interior: 'Here',777 },778 },779 testAmount,780 );781782 let maliciousXcmProgramFullIdSent: any;783 let maliciousXcmProgramHereIdSent: any;784 const maxWaitBlocks = 3;785786 787 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {788 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);789790 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);791 });792793 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash794 && event.outcome.isUntrustedReserveLocation);795796 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);797 expect(accountBalance).to.be.equal(0n);798799 800 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {801 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);802803 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);804 });805806 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash807 && event.outcome.isUntrustedReserveLocation);808809 accountBalance = await helper.balance.getSubstrate(targetAccount.address);810 expect(accountBalance).to.be.equal(0n);811 });812});813814describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {815 let alice: IKeyringPair;816 let randomAccount: IKeyringPair;817 let unqFees: bigint;818 let balanceUniqueTokenInit: bigint;819 let balanceUniqueTokenMiddle: bigint;820 let balanceUniqueTokenFinal: bigint;821 const maxWaitBlocks = 6;822823 const uniqueAssetId = {824 Concrete: {825 parents: 1,826 interior: {827 X1: {828 Parachain: UNIQUE_CHAIN,829 },830 },831 },832 };833834 before(async () => {835 await usingPlaygrounds(async (helper, privateKey) => {836 alice = await privateKey('//Alice');837 [randomAccount] = await helper.arrange.createAccounts([0n], alice);838839 840 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);841 });842843 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {844 const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))845 .toJSON() as [])846 .map(nToBigInt).length != 0;847 848849850851852853 if(!isWhitelisted) {854 await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);855 }856857 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);858 });859860 await usingPlaygrounds(async (helper) => {861 await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);862 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);863 });864 });865866 itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {867868 const destination = {869 V2: {870 parents: 1,871 interior: {872 X1: {873 Parachain: POLKADEX_CHAIN,874 },875 },876 },877 };878879 const beneficiary = {880 V2: {881 parents: 0,882 interior: {883 X1: {884 AccountId32: {885 network: 'Any',886 id: randomAccount.addressRaw,887 },888 },889 },890 },891 };892893 const assets = {894 V2: [895 {896 id: {897 Concrete: {898 parents: 0,899 interior: 'Here',900 },901 },902 fun: {903 Fungible: TRANSFER_AMOUNT,904 },905 },906 ],907 };908909 const feeAssetItem = 0;910911 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');912 const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);913 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);914915 unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;916 console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));917 expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;918919 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {920 921922923924925926927928929930 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);931 });932 });933934935 itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {936937 const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(938 randomAccount.addressRaw,939 {940 Concrete: {941 parents: 1,942 interior: {943 X1: {Parachain: UNIQUE_CHAIN},944 },945 },946 },947 TRANSFER_AMOUNT,948 );949950 let xcmProgramSent: any;951952953 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {954 await helper.getSudo().xcm.send(alice, uniqueMultilocation, xcmProgram);955956 xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);957 });958959 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);960961 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);962963 expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);964 });965966 itSub('Polkadex can send only up to its balance', async ({helper}) => {967 const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);968 const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);969 await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);970 const moreThanPolkadexHas = 2n * polkadexBalance;971972 const targetAccount = helper.arrange.createEmptyAccount();973974 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(975 targetAccount.addressRaw,976 {977 Concrete: {978 parents: 0,979 interior: 'Here',980 },981 },982 moreThanPolkadexHas,983 );984985 let maliciousXcmProgramSent: any;986987988 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {989 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);990991 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);992 });993994 await expectFailedToTransact(helper, maliciousXcmProgramSent);995996 const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);997 expect(targetAccountBalance).to.be.equal(0n);998 });9991000 itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {1001 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1002 const targetAccount = helper.arrange.createEmptyAccount();10031004 const uniqueMultilocation = {1005 V2: {1006 parents: 1,1007 interior: {1008 X1: {1009 Parachain: UNIQUE_CHAIN,1010 },1011 },1012 },1013 };10141015 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1016 targetAccount.addressRaw,1017 {1018 Concrete: {1019 parents: 1,1020 interior: {1021 X1: {1022 Parachain: UNIQUE_CHAIN,1023 },1024 },1025 },1026 },1027 testAmount,1028 );10291030 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1031 targetAccount.addressRaw,1032 {1033 Concrete: {1034 parents: 0,1035 interior: 'Here',1036 },1037 },1038 testAmount,1039 );10401041 let maliciousXcmProgramFullIdSent: any;1042 let maliciousXcmProgramHereIdSent: any;1043 const maxWaitBlocks = 3;10441045 1046 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1047 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);10481049 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1050 });10511052 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);10531054 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1055 expect(accountBalance).to.be.equal(0n);10561057 1058 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1059 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);10601061 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1062 });10631064 await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);10651066 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1067 expect(accountBalance).to.be.equal(0n);1068 });1069});1070107110721073describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {1074 let alice: IKeyringPair;1075 let alith: IKeyringPair;10761077 const testAmount = 100_000_000_000n;1078 let uniqueParachainJunction;1079 let uniqueAccountJunction;10801081 let uniqueParachainMultilocation: any;1082 let uniqueAccountMultilocation: any;1083 let uniqueCombinedMultilocation: any;1084 let uniqueCombinedMultilocationAcala: any; 10851086 let messageSent: any;10871088 const maxWaitBlocks = 3;10891090 before(async () => {1091 await usingPlaygrounds(async (helper, privateKey) => {1092 alice = await privateKey('//Alice');10931094 uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};1095 uniqueAccountJunction = {1096 AccountId32: {1097 network: 'Any',1098 id: alice.addressRaw,1099 },1100 };11011102 uniqueParachainMultilocation = {1103 V2: {1104 parents: 1,1105 interior: {1106 X1: uniqueParachainJunction,1107 },1108 },1109 };11101111 uniqueAccountMultilocation = {1112 V2: {1113 parents: 0,1114 interior: {1115 X1: uniqueAccountJunction,1116 },1117 },1118 };11191120 uniqueCombinedMultilocation = {1121 V2: {1122 parents: 1,1123 interior: {1124 X2: [uniqueParachainJunction, uniqueAccountJunction],1125 },1126 },1127 };11281129 uniqueCombinedMultilocationAcala = {1130 V2: {1131 parents: 1,1132 interior: {1133 X2: [uniqueParachainJunction, uniqueAccountJunction],1134 },1135 },1136 };11371138 1139 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1140 });11411142 1143 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1144 alith = helper.account.alithAccount();1145 });1146 });1147114811491150 itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {1151 await usingAcalaPlaygrounds(acalaUrl, async (helper) => {1152 const id = {1153 Token: 'ACA',1154 };1155 const destination = uniqueCombinedMultilocationAcala;1156 await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');11571158 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1159 });11601161 await expectFailedToTransact(helper, messageSent);1162 });11631164 itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {1165 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1166 const id = 'SelfReserve';1167 const destination = uniqueCombinedMultilocation;1168 await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');11691170 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1171 });11721173 await expectFailedToTransact(helper, messageSent);1174 });11751176 itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {1177 await usingAstarPlaygrounds(astarUrl, async (helper) => {1178 const destinationParachain = uniqueParachainMultilocation;1179 const beneficiary = uniqueAccountMultilocation;1180 const assets = {1181 V2: [{1182 id: {1183 Concrete: {1184 parents: 0,1185 interior: 'Here',1186 },1187 },1188 fun: {1189 Fungible: testAmount,1190 },1191 }],1192 };1193 const feeAssetItem = 0;11941195 await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [1196 destinationParachain,1197 beneficiary,1198 assets,1199 feeAssetItem,1200 ]);12011202 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1203 });12041205 await expectFailedToTransact(helper, messageSent);1206 });12071208 itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {12091210 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1211 helper.arrange.createEmptyAccount().addressRaw,1212 {1213 Concrete: {1214 parents: 1,1215 interior: {1216 X1: {1217 Parachain: POLKADEX_CHAIN,1218 },1219 },1220 },1221 },1222 testAmount,1223 );12241225 await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {1226 await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);1227 messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1228 });12291230 await expectFailedToTransact(helper, messageSent);1231 });1232});12331234describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {1235 1236 let alice: IKeyringPair;1237 let uniqueAssetLocation;12381239 let randomAccountUnique: IKeyringPair;1240 let randomAccountMoonbeam: IKeyringPair;12411242 1243 let assetId: string;12441245 const uniqueAssetMetadata = {1246 name: 'xcUnique',1247 symbol: 'xcUNQ',1248 decimals: 18,1249 isFrozen: false,1250 minimalBalance: 1n,1251 };12521253 let balanceUniqueTokenInit: bigint;1254 let balanceUniqueTokenMiddle: bigint;1255 let balanceUniqueTokenFinal: bigint;1256 let balanceForeignUnqTokenInit: bigint;1257 let balanceForeignUnqTokenMiddle: bigint;1258 let balanceForeignUnqTokenFinal: bigint;1259 let balanceGlmrTokenInit: bigint;1260 let balanceGlmrTokenMiddle: bigint;1261 let balanceGlmrTokenFinal: bigint;12621263 before(async () => {1264 await usingPlaygrounds(async (helper, privateKey) => {1265 alice = await privateKey('//Alice');1266 [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);12671268 balanceForeignUnqTokenInit = 0n;12691270 1271 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1272 });12731274 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1275 const alithAccount = helper.account.alithAccount();1276 const baltatharAccount = helper.account.baltatharAccount();1277 const dorothyAccount = helper.account.dorothyAccount();12781279 randomAccountMoonbeam = helper.account.create();12801281 1282 console.log('Sponsoring Dorothy.......');1283 await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);1284 console.log('Sponsoring Dorothy.......DONE');1285 12861287 uniqueAssetLocation = {1288 XCM: {1289 parents: 1,1290 interior: {X1: {Parachain: UNIQUE_CHAIN}},1291 },1292 };1293 const existentialDeposit = 1n;1294 const isSufficient = true;1295 const unitsPerSecond = 1n;1296 const numAssetsWeightHint = 0;12971298 if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {1299 console.log('Unique asset is already registered on MoonBeam');1300 } else {1301 const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({1302 location: uniqueAssetLocation,1303 metadata: uniqueAssetMetadata,1304 existentialDeposit,1305 isSufficient,1306 unitsPerSecond,1307 numAssetsWeightHint,1308 });13091310 console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);13111312 await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);13131314 1315 console.log('Acquire Unique AssetId Info on Moonbeam.......');13161317 assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();13181319 console.log('UNQ asset ID is %s', assetId);1320 console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');1321 }1322 13231324 1325 console.log('Sponsoring random Account.......');1326 await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);1327 console.log('Sponsoring random Account.......DONE');1328 13291330 balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);1331 });13321333 await usingPlaygrounds(async (helper) => {1334 await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);1335 balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);1336 });1337 });13381339 itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {1340 const currencyId = {1341 NativeAssetId: 'Here',1342 };1343 const dest = {1344 V2: {1345 parents: 1,1346 interior: {1347 X2: [1348 {Parachain: MOONBEAM_CHAIN},1349 {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},1350 ],1351 },1352 },1353 };1354 const amount = TRANSFER_AMOUNT;13551356 await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');13571358 balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);1359 expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;13601361 const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;1362 console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));1363 expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;13641365 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1366 await helper.wait.newBlocks(3);1367 balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);13681369 const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;1370 console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1371 expect(glmrFees == 0n).to.be.true;13721373 balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;13741375 const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;1376 console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));1377 expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;1378 });1379 });13801381 itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {1382 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1383 const asset = {1384 V2: {1385 id: {1386 Concrete: {1387 parents: 1,1388 interior: {1389 X1: {Parachain: UNIQUE_CHAIN},1390 },1391 },1392 },1393 fun: {1394 Fungible: TRANSFER_AMOUNT,1395 },1396 },1397 };1398 const destination = {1399 V2: {1400 parents: 1,1401 interior: {1402 X2: [1403 {Parachain: UNIQUE_CHAIN},1404 {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},1405 ],1406 },1407 },1408 };14091410 await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');14111412 balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);14131414 const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;1415 console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));1416 expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;14171418 const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);14191420 expect(unqRandomAccountAsset).to.be.null;14211422 balanceForeignUnqTokenFinal = 0n;14231424 const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;1425 console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));1426 expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;1427 });14281429 await helper.wait.newBlocks(3);14301431 balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);1432 const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;1433 expect(actuallyDelivered > 0).to.be.true;14341435 console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));14361437 const unqFees = TRANSFER_AMOUNT - actuallyDelivered;1438 console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));1439 expect(unqFees == 0n).to.be.true;1440 });14411442 itSub('Moonbeam can send only up to its balance', async ({helper}) => {1443 1444 const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);1445 const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);1446 await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);14471448 const moreThanMoonbeamHas = moonbeamBalance * 2n;14491450 let targetAccountBalance = 0n;1451 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);14521453 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1454 targetAccount.addressRaw,1455 {1456 Concrete: {1457 parents: 0,1458 interior: 'Here',1459 },1460 },1461 moreThanMoonbeamHas,1462 );14631464 let maliciousXcmProgramSent: any;1465 const maxWaitBlocks = 3;14661467 1468 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1469 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgram]);14701471 1472 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1473 await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);14741475 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1476 });14771478 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1479 && event.outcome.isFailedToTransactAsset);14801481 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1482 expect(targetAccountBalance).to.be.equal(0n);14831484 1485 const validTransferAmount = moonbeamBalance / 2n;1486 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1487 targetAccount.addressRaw,1488 {1489 Concrete: {1490 parents: 0,1491 interior: 'Here',1492 },1493 },1494 validTransferAmount,1495 );14961497 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1498 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, validXcmProgram]);14991500 1501 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1502 await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);1503 });15041505 await helper.wait.newBlocks(maxWaitBlocks);15061507 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1508 expect(targetAccountBalance).to.be.equal(validTransferAmount);1509 });15101511 itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {1512 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1513 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);15141515 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1516 targetAccount.addressRaw,1517 {1518 Concrete: {1519 parents: 0,1520 interior: {1521 X1: {1522 Parachain: UNIQUE_CHAIN,1523 },1524 },1525 },1526 },1527 testAmount,1528 );15291530 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1531 targetAccount.addressRaw,1532 {1533 Concrete: {1534 parents: 0,1535 interior: 'Here',1536 },1537 },1538 testAmount,1539 );15401541 let maliciousXcmProgramFullIdSent: any;1542 let maliciousXcmProgramHereIdSent: any;1543 const maxWaitBlocks = 3;15441545 1546 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1547 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramFullId]);15481549 1550 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1551 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);15521553 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1554 });15551556 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1557 && event.outcome.isUntrustedReserveLocation);15581559 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1560 expect(accountBalance).to.be.equal(0n);15611562 1563 await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {1564 const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueMultilocation, maliciousXcmProgramHereId]);15651566 1567 const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);1568 await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);15691570 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1571 });15721573 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1574 && event.outcome.isUntrustedReserveLocation);15751576 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1577 expect(accountBalance).to.be.equal(0n);1578 });1579});15801581describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {1582 let alice: IKeyringPair;1583 let randomAccount: IKeyringPair;15841585 const UNQ_ASSET_ID_ON_ASTAR = 1;1586 const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n;15871588 1589 const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); 1590 const unitsPerSecond = 228_000_000_000n; 1591 const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); 1592 const unqToAstarArrived = 9_999_999_999_088_000_000n; 15931594 1595 const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); 1596 const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; 15971598 let balanceAfterUniqueToAstarXCM: bigint;15991600 before(async () => {1601 await usingPlaygrounds(async (helper, privateKey) => {1602 alice = await privateKey('//Alice');1603 [randomAccount] = await helper.arrange.createAccounts([100n], alice);1604 console.log('randomAccount', randomAccount.address);16051606 1607 await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);1608 });16091610 await usingAstarPlaygrounds(astarUrl, async (helper) => {1611 if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {1612 console.log('1. Create foreign asset and metadata');1613 1614 await helper.assets.create(1615 alice,1616 UNQ_ASSET_ID_ON_ASTAR,1617 alice.address,1618 UNQ_MINIMAL_BALANCE_ON_ASTAR,1619 );16201621 await helper.assets.setMetadata(1622 alice,1623 UNQ_ASSET_ID_ON_ASTAR,1624 'Cross chain UNQ',1625 'xcUNQ',1626 Number(UNQ_DECIMALS),1627 );16281629 console.log('2. Register asset location on Astar');1630 const assetLocation = {1631 V2: {1632 parents: 1,1633 interior: {1634 X1: {1635 Parachain: UNIQUE_CHAIN,1636 },1637 },1638 },1639 };16401641 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);16421643 console.log('3. Set UNQ payment for XCM execution on Astar');1644 await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);1645 }1646 console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');1647 await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);1648 });1649 });16501651 itSub('Should connect and send UNQ to Astar', async ({helper}) => {1652 const destination = {1653 V2: {1654 parents: 1,1655 interior: {1656 X1: {1657 Parachain: ASTAR_CHAIN,1658 },1659 },1660 },1661 };16621663 const beneficiary = {1664 V2: {1665 parents: 0,1666 interior: {1667 X1: {1668 AccountId32: {1669 network: 'Any',1670 id: randomAccount.addressRaw,1671 },1672 },1673 },1674 },1675 };16761677 const assets = {1678 V2: [1679 {1680 id: {1681 Concrete: {1682 parents: 0,1683 interior: 'Here',1684 },1685 },1686 fun: {1687 Fungible: unqToAstarTransferred,1688 },1689 },1690 ],1691 };16921693 1694 const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);1695 console.log(`Initial balance is: ${balanceBefore}`);16961697 const feeAssetItem = 0;1698 await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');16991700 1701 balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);1702 console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);1703 console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);1704 expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;17051706 await usingAstarPlaygrounds(astarUrl, async (helper) => {1707 await helper.wait.newBlocks(3);1708 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1709 const astarBalance = await helper.balance.getSubstrate(randomAccount.address);17101711 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);1712 console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);17131714 expect(xcUNQbalance).to.eq(unqToAstarArrived);1715 1716 expect(astarBalance).to.eq(astarInitialBalance);1717 });1718 });17191720 itSub('Should connect to Astar and send UNQ back', async ({helper}) => {1721 await usingAstarPlaygrounds(astarUrl, async (helper) => {1722 const destination = {1723 V2: {1724 parents: 1,1725 interior: {1726 X1: {1727 Parachain: UNIQUE_CHAIN,1728 },1729 },1730 },1731 };17321733 const beneficiary = {1734 V2: {1735 parents: 0,1736 interior: {1737 X1: {1738 AccountId32: {1739 network: 'Any',1740 id: randomAccount.addressRaw,1741 },1742 },1743 },1744 },1745 };17461747 const assets = {1748 V2: [1749 {1750 id: {1751 Concrete: {1752 parents: 1,1753 interior: {1754 X1: {1755 Parachain: UNIQUE_CHAIN,1756 },1757 },1758 },1759 },1760 fun: {1761 Fungible: unqFromAstarTransfered,1762 },1763 },1764 ],1765 };17661767 1768 const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);1769 console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);1770 expect(balanceASTRbefore).to.eq(astarInitialBalance);17711772 const feeAssetItem = 0;1773 1774 await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);17751776 const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);1777 const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);1778 console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);17791780 1781 expect(xcUNQbalance).to.eq(unqOnAstarLeft);1782 1783 expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);1784 });17851786 await helper.wait.newBlocks(3);1787 const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);1788 console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);1789 expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);1790 });17911792 itSub('Astar can send only up to its balance', async ({helper}) => {1793 1794 const astarBalance = 10000n * (10n ** UNQ_DECIMALS);1795 const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);1796 await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);17971798 const moreThanAstarHas = astarBalance * 2n;17991800 let targetAccountBalance = 0n;1801 const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);18021803 const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1804 targetAccount.addressRaw,1805 {1806 Concrete: {1807 parents: 0,1808 interior: 'Here',1809 },1810 },1811 moreThanAstarHas,1812 );18131814 let maliciousXcmProgramSent: any;1815 const maxWaitBlocks = 3;18161817 1818 await usingAstarPlaygrounds(astarUrl, async (helper) => {1819 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgram);18201821 maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1822 });18231824 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash1825 && event.outcome.isFailedToTransactAsset);18261827 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1828 expect(targetAccountBalance).to.be.equal(0n);18291830 1831 const validTransferAmount = astarBalance / 2n;1832 const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(1833 targetAccount.addressRaw,1834 {1835 Concrete: {1836 parents: 0,1837 interior: 'Here',1838 },1839 },1840 validTransferAmount,1841 );18421843 await usingAstarPlaygrounds(astarUrl, async (helper) => {1844 await helper.getSudo().xcm.send(alice, uniqueMultilocation, validXcmProgram);1845 });18461847 await helper.wait.newBlocks(maxWaitBlocks);18481849 targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);1850 expect(targetAccountBalance).to.be.equal(validTransferAmount);1851 });18521853 itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {1854 const testAmount = 10_000n * (10n ** UNQ_DECIMALS);1855 const [targetAccount] = await helper.arrange.createAccounts([0n], alice);18561857 const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(1858 targetAccount.addressRaw,1859 {1860 Concrete: {1861 parents: 1,1862 interior: {1863 X1: {1864 Parachain: UNIQUE_CHAIN,1865 },1866 },1867 },1868 },1869 testAmount,1870 );18711872 const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(1873 targetAccount.addressRaw,1874 {1875 Concrete: {1876 parents: 0,1877 interior: 'Here',1878 },1879 },1880 testAmount,1881 );18821883 let maliciousXcmProgramFullIdSent: any;1884 let maliciousXcmProgramHereIdSent: any;1885 const maxWaitBlocks = 3;18861887 1888 await usingAstarPlaygrounds(astarUrl, async (helper) => {1889 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);18901891 maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1892 });18931894 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash1895 && event.outcome.isUntrustedReserveLocation);18961897 let accountBalance = await helper.balance.getSubstrate(targetAccount.address);1898 expect(accountBalance).to.be.equal(0n);18991900 1901 await usingAstarPlaygrounds(astarUrl, async (helper) => {1902 await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);19031904 maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);1905 });19061907 await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash1908 && event.outcome.isUntrustedReserveLocation);19091910 accountBalance = await helper.balance.getSubstrate(targetAccount.address);1911 expect(accountBalance).to.be.equal(0n);1912 });1913});